-
% else:
No stats to show.
diff --git a/init-scripts/init.freebsd b/init-scripts/init.freebsd
index 3e3a9d90..b3a62fee 100755
--- a/init-scripts/init.freebsd
+++ b/init-scripts/init.freebsd
@@ -33,12 +33,13 @@ load_rc_config ${name}
: ${plexpy_dir:="/usr/local/plexpy"}
: ${plexpy_chdir:="${plexpy_dir}"}
: ${plexpy_pid:="${plexpy_dir}/plexpy.pid"}
+: ${plexpy_flags:=""}
status_cmd="${name}_status"
stop_cmd="${name}_stop"
-command="/usr/sbin/daemon"
-command_args="python2 ${plexpy_dir}/PlexPy.py --daemon --pidfile ${plexpy_pid} --quiet --nolaunch"
+command="${plexpy_dir}/PlexPy.py"
+command_args="--daemon --pidfile ${plexpy_pid} --quiet --nolaunch ${plexpy_flags}"
# Ensure user is root when running this script.
if [ `id -u` != "0" ]; then
diff --git a/init-scripts/init.freenas b/init-scripts/init.freenas
old mode 100644
new mode 100755
index e2946f2b..54587aa8
--- a/init-scripts/init.freenas
+++ b/init-scripts/init.freenas
@@ -33,12 +33,13 @@ load_rc_config ${name}
: ${plexpy_dir:="/usr/local/share/plexpy"}
: ${plexpy_chdir:="${plexpy_dir}"}
: ${plexpy_pid:="${plexpy_dir}/plexpy.pid"}
+: ${plexpy_flags:=""}
status_cmd="${name}_status"
stop_cmd="${name}_stop"
-command="/usr/sbin/daemon"
-command_args="python2 ${plexpy_dir}/PlexPy.py --daemon --pidfile ${plexpy_pid} --quiet --nolaunch"
+command="${plexpy_dir}/PlexPy.py"
+command_args="--daemon --pidfile ${plexpy_pid} --quiet --nolaunch ${plexpy_flags}"
# Ensure user is root when running this script.
if [ `id -u` != "0" ]; then
diff --git a/plexpy/__init__.py b/plexpy/__init__.py
index 82203de5..5d32d075 100644
--- a/plexpy/__init__.py
+++ b/plexpy/__init__.py
@@ -103,7 +103,7 @@ def initialize(config_file):
if not CONFIG.HTTPS_KEY:
CONFIG.HTTPS_KEY = os.path.join(DATA_DIR, 'server.key')
- if not CONFIG.LOG_DIR.startswith(os.path.abspath(DATA_DIR)):
+ if not CONFIG.LOG_DIR:
CONFIG.LOG_DIR = os.path.join(DATA_DIR, 'logs')
if not os.path.exists(CONFIG.LOG_DIR):
@@ -120,8 +120,7 @@ def initialize(config_file):
logger.initLogger(console=not QUIET, log_dir=CONFIG.LOG_DIR,
verbose=VERBOSE)
- if not CONFIG.BACKUP_DIR.startswith(os.path.abspath(DATA_DIR)):
- # Put the backup dir in the data dir for now
+ if not CONFIG.BACKUP_DIR:
CONFIG.BACKUP_DIR = os.path.join(DATA_DIR, 'backups')
if not os.path.exists(CONFIG.BACKUP_DIR):
try:
@@ -129,14 +128,13 @@ def initialize(config_file):
except OSError as e:
logger.error("Could not create backup dir '%s': %s", BACKUP_DIR, e)
- if not CONFIG.CACHE_DIR.startswith(os.path.abspath(DATA_DIR)):
- # Put the cache dir in the data dir for now
+ if not CONFIG.CACHE_DIR:
CONFIG.CACHE_DIR = os.path.join(DATA_DIR, 'cache')
if not os.path.exists(CONFIG.CACHE_DIR):
try:
os.makedirs(CONFIG.CACHE_DIR)
except OSError as e:
- logger.error("Could not create cache dir '%s': %s", DATA_DIR, e)
+ logger.error("Could not create cache dir '%s': %s", CACHE_DIR, e)
# Initialize the database
logger.info('Checking to see if the database has all tables....')
@@ -306,6 +304,13 @@ def initialize_scheduler():
schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
hours=0, minutes=0, seconds=0)
+ if CONFIG.MONITOR_PMS_UPDATES:
+ schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates',
+ hours=12, minutes=0, seconds=0)
+ else:
+ schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates',
+ hours=0, minutes=0, seconds=0)
+
if CONFIG.MONITOR_REMOTE_ACCESS:
schedule_job(activity_pinger.check_server_response, 'Check for Plex remote access',
hours=0, minutes=0, seconds=seconds)
@@ -390,7 +395,7 @@ def dbcheck():
# sessions table :: This is a temp table that logs currently active sessions
c_db.execute(
'CREATE TABLE IF NOT EXISTS sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, '
- 'session_key INTEGER, rating_key INTEGER, section_id INTEGER, media_type TEXT, started INTEGER, '
+ 'session_key INTEGER, rating_key INTEGER, section_id INTEGER, media_type TEXT, started INTEGER, stopped INTEGER, '
'paused_counter INTEGER DEFAULT 0, state TEXT, user_id INTEGER, user TEXT, friendly_name TEXT, '
'ip_address TEXT, machine_id TEXT, player TEXT, platform TEXT, title TEXT, parent_title TEXT, '
'grandparent_title TEXT, parent_rating_key INTEGER, grandparent_rating_key INTEGER, '
@@ -413,8 +418,8 @@ def dbcheck():
# session_history_media_info table :: This is a table which logs each session's media info
c_db.execute(
- 'CREATE TABLE IF NOT EXISTS session_history_media_info (id INTEGER PRIMARY KEY, '
- 'rating_key INTEGER, video_decision TEXT, audio_decision TEXT, duration INTEGER DEFAULT 0, width INTEGER, '
+ 'CREATE TABLE IF NOT EXISTS session_history_media_info (id INTEGER PRIMARY KEY, rating_key INTEGER, '
+ 'video_decision TEXT, audio_decision TEXT, transcode_decision TEXT, duration INTEGER DEFAULT 0, width INTEGER, '
'height INTEGER, container TEXT, video_codec TEXT, audio_codec TEXT, bitrate INTEGER, video_resolution TEXT, '
'video_framerate TEXT, aspect_ratio TEXT, audio_channels INTEGER, transcode_protocol TEXT, '
'transcode_container TEXT, transcode_video_codec TEXT, transcode_audio_codec TEXT, '
@@ -614,6 +619,15 @@ def dbcheck():
'ALTER TABLE sessions ADD COLUMN section_id INTEGER'
)
+ # Upgrade sessions table from earlier versions
+ try:
+ c_db.execute('SELECT stopped FROM sessions')
+ except sqlite3.OperationalError:
+ logger.debug(u"Altering database. Updating database table sessions.")
+ c_db.execute(
+ 'ALTER TABLE sessions ADD COLUMN stopped INTEGER'
+ )
+
# Upgrade session_history table from earlier versions
try:
c_db.execute('SELECT reference_id FROM session_history')
@@ -664,6 +678,21 @@ def dbcheck():
'ALTER TABLE session_history_metadata ADD COLUMN section_id INTEGER'
)
+ # Upgrade session_history_media_info table from earlier versions
+ try:
+ c_db.execute('SELECT transcode_decision FROM session_history_media_info')
+ except sqlite3.OperationalError:
+ logger.debug(u"Altering database. Updating database table session_history_media_info.")
+ c_db.execute(
+ 'ALTER TABLE session_history_media_info ADD COLUMN transcode_decision TEXT'
+ )
+ c_db.execute(
+ 'UPDATE session_history_media_info SET transcode_decision = (CASE '
+ 'WHEN video_decision = "transcode" OR audio_decision = "transcode" THEN "transcode" '
+ 'WHEN video_decision = "copy" OR audio_decision = "copy" THEN "copy" '
+ 'WHEN video_decision = "direct play" OR audio_decision = "direct play" THEN "direct play" END)'
+ )
+
# Upgrade users table from earlier versions
try:
c_db.execute('SELECT do_notify FROM users')
@@ -890,7 +919,13 @@ def shutdown(restart=False, update=False):
if '--nolaunch' not in args:
args += ['--nolaunch']
logger.info('Restarting PlexPy with %s', args)
- os.execv(exe, args)
+
+ # os.execv fails with spaced names on Windows
+ # https://bugs.python.org/issue19066
+ if os.name == 'nt':
+ subprocess.Popen(args, cwd=os.getcwd())
+ else:
+ os.execv(exe, args)
os._exit(0)
diff --git a/plexpy/activity_handler.py b/plexpy/activity_handler.py
index fe6de42f..c5bca198 100644
--- a/plexpy/activity_handler.py
+++ b/plexpy/activity_handler.py
@@ -16,7 +16,7 @@
import time
import plexpy
-from plexpy import logger, pmsconnect, activity_processor, threading, notification_handler, helpers
+from plexpy import logger, pmsconnect, activity_processor, threading, notification_handler, helpers, notifiers
class ActivityHandler(object):
@@ -57,9 +57,11 @@ class ActivityHandler(object):
if self.is_valid_session() and self.get_live_session():
logger.debug(u"PlexPy ActivityHandler :: Session %s has started." % str(self.get_session_key()))
- # Fire off notifications
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=self.get_live_session(), notify_action='play')).start()
+ # Check if any notification agents have notifications enabled
+ if any(d['on_play'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify,
+ kwargs=dict(stream_data=self.get_live_session(), notify_action='play')).start()
# Write the new session to our temp session table
self.update_db_session()
@@ -77,20 +79,24 @@ class ActivityHandler(object):
if not force_stop:
ap.set_session_state(session_key=self.get_session_key(),
state=self.timeline['state'],
- view_offset=self.timeline['viewOffset'])
+ view_offset=self.timeline['viewOffset'],
+ stopped=int(time.time()))
# Retrieve the session data from our temp table
db_session = ap.get_session_by_key(session_key=self.get_session_key())
- # Fire off notifications
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=db_session, notify_action='stop')).start()
+ # Check if any notification agents have notifications enabled
+ if any(d['on_stop'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify,
+ kwargs=dict(stream_data=db_session, notify_action='stop')).start()
# Write it to the history table
monitor_proc = activity_processor.ActivityProcessor()
monitor_proc.write_session_history(session=db_session)
# Remove the session from our temp session table
+ logger.debug(u"PlexPy ActivityHandler :: Removing session %s from session queue" % str(self.get_session_key()))
ap.delete_session(session_key=self.get_session_key())
def on_pause(self):
@@ -109,9 +115,11 @@ class ActivityHandler(object):
# Retrieve the session data from our temp table
db_session = ap.get_session_by_key(session_key=self.get_session_key())
- # Fire off notifications
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=db_session, notify_action='pause')).start()
+ # Check if any notification agents have notifications enabled
+ if any(d['on_pause'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify,
+ kwargs=dict(stream_data=db_session, notify_action='pause')).start()
def on_resume(self):
if self.is_valid_session():
@@ -129,9 +137,11 @@ class ActivityHandler(object):
# Retrieve the session data from our temp table
db_session = ap.get_session_by_key(session_key=self.get_session_key())
- # Fire off notifications
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=db_session, notify_action='resume')).start()
+ # Check if any notification agents have notifications enabled
+ if any(d['on_resume'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify,
+ kwargs=dict(stream_data=db_session, notify_action='resume')).start()
def on_buffer(self):
if self.is_valid_session():
@@ -159,8 +169,11 @@ class ActivityHandler(object):
if plexpy.CONFIG.BUFFER_THRESHOLD > 0 and (current_buffer_count >= plexpy.CONFIG.BUFFER_THRESHOLD and \
time_since_last_trigger == 0 or time_since_last_trigger >= plexpy.CONFIG.BUFFER_WAIT):
ap.set_session_buffer_trigger_time(session_key=self.get_session_key())
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=db_stream, notify_action='buffer')).start()
+
+ # Check if any notification agents have notifications enabled
+ if any(d['on_buffer'] for d in notifiers.available_notification_agents()):
+ threading.Thread(target=notification_handler.notify,
+ kwargs=dict(stream_data=db_stream, notify_action='buffer')).start()
# This function receives events from our websocket connection
def process(self):
@@ -202,10 +215,17 @@ class ActivityHandler(object):
# Monitor if the stream has reached the watch percentage for notifications
# The only purpose of this is for notifications
- progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration'])
- if progress_percent >= plexpy.CONFIG.NOTIFY_WATCHED_PERCENT and this_state != 'buffering':
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=db_session, notify_action='watched')).start()
+ # Check if any notification agents have notifications enabled
+ notify_agents = [d['id'] for d in notifiers.available_notification_agents() if d['on_watched']]
+ # Get the current states for notifications from our db
+ notified_agents = [d['agent_id'] for d in notification_handler.get_notify_state(session=db_session)
+ if d['notify_action'] == 'watched'] if notify_agents else []
+
+ if any(a not in notified_agents for a in notify_agents):
+ progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration'])
+ if progress_percent >= plexpy.CONFIG.NOTIFY_WATCHED_PERCENT and this_state != 'buffering':
+ # Rather not put this on it's own thread so we know it completes before our next event.
+ notification_handler.notify(stream_data=db_session, notify_action='watched')
else:
# We don't have this session in our table yet, start a new one.
diff --git a/plexpy/activity_pinger.py b/plexpy/activity_pinger.py
index 84474577..b5bfc05a 100644
--- a/plexpy/activity_pinger.py
+++ b/plexpy/activity_pinger.py
@@ -13,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, pmsconnect, plextv, notification_handler, database, helpers, activity_processor, libraries
+from plexpy import logger, pmsconnect, plextv, notification_handler, database, helpers, activity_processor, libraries, notifiers
import threading
import plexpy
@@ -38,9 +38,12 @@ def check_active_sessions(ws_request=False):
if session_list:
if int_ping_count >= 3:
logger.info(u"PlexPy Monitor :: The Plex Media Server is back up.")
- # Fire off notifications
- threading.Thread(target=notification_handler.notify_timeline,
- kwargs=dict(notify_action='intup')).start()
+
+ # Check if any notification agents have notifications enabled
+ if any(d['on_intup'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify_timeline,
+ kwargs=dict(notify_action='intup')).start()
int_ping_count = 0
media_container = session_list['sessions']
@@ -58,16 +61,24 @@ def check_active_sessions(ws_request=False):
# Here we can check the play states
if session['state'] != stream['state']:
if session['state'] == 'paused':
- # Push any notifications -
- # Push it on it's own thread so we don't hold up our db actions
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=stream, notify_action='pause')).start()
+ logger.debug(u"PlexPy Monitor :: Session %s has been paused." % stream['session_key'])
+
+ # Check if any notification agents have notifications enabled
+ if any(d['on_pause'] for d in notifiers.available_notification_agents()):
+ # Push any notifications -
+ # Push it on it's own thread so we don't hold up our db actions
+ threading.Thread(target=notification_handler.notify,
+ kwargs=dict(stream_data=stream, notify_action='pause')).start()
if session['state'] == 'playing' and stream['state'] == 'paused':
- # Push any notifications -
- # Push it on it's own thread so we don't hold up our db actions
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=stream, notify_action='resume')).start()
+ logger.debug(u"PlexPy Monitor :: Session %s has been resumed." % stream['session_key'])
+
+ # Check if any notification agents have notifications enabled
+ if any(d['on_resume'] for d in notifiers.available_notification_agents()):
+ # Push any notifications -
+ # Push it on it's own thread so we don't hold up our db actions
+ threading.Thread(target=notification_handler.notify,
+ kwargs=dict(stream_data=stream, notify_action='resume')).start()
if stream['state'] == 'paused' and not ws_request:
# The stream is still paused so we need to increment the paused_counter
@@ -105,8 +116,12 @@ def check_active_sessions(ws_request=False):
'WHERE session_key = ? AND rating_key = ?',
[stream['session_key'], stream['rating_key']])
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=stream, notify_action='buffer')).start()
+ # Check if any notification agents have notifications enabled
+ if any(d['on_buffer'] for d in notifiers.available_notification_agents()):
+ # Push any notifications -
+ # Push it on it's own thread so we don't hold up our db actions
+ threading.Thread(target=notification_handler.notify,
+ kwargs=dict(stream_data=stream, notify_action='buffer')).start()
else:
# Subsequent buffer notifications after wait time
if int(time.time()) > buffer_values[0]['buffer_last_triggered'] + \
@@ -119,11 +134,16 @@ def check_active_sessions(ws_request=False):
'WHERE session_key = ? AND rating_key = ?',
[stream['session_key'], stream['rating_key']])
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=stream, notify_action='buffer')).start()
+ # Check if any notification agents have notifications enabled
+ if any(d['on_buffer'] for d in notifiers.available_notification_agents()):
+ # Push any notifications -
+ # Push it on it's own thread so we don't hold up our db actions
+ threading.Thread(target=notification_handler.notify,
+ 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]['buffer_count'],
+ logger.debug(u"PlexPy Monitor :: Session %s is buffering. Count is now %s. Last triggered %s."
+ % (stream['session_key'],
+ 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
@@ -132,37 +152,61 @@ def check_active_sessions(ws_request=False):
if session['view_offset'] and session['duration'] and session['state'] != 'buffering':
if helpers.get_percent(session['view_offset'],
session['duration']) > plexpy.CONFIG.NOTIFY_WATCHED_PERCENT:
+ # Check if any notification agents have notifications enabled
+ if any(d['on_watched'] for d in notifiers.available_notification_agents()):
+ # Push any notifications -
+ # Push it on it's own thread so we don't hold up our db actions
+ threading.Thread(target=notification_handler.notify,
+ kwargs=dict(stream_data=stream, notify_action='watched')).start()
+
+ else:
+ # The user has stopped playing a stream
+ if stream['state'] != 'stopped':
+ logger.debug(u"PlexPy Monitor :: Session %s has stopped." % stream['session_key'])
+
+ # Set the stream stop time
+ stream['stopped'] = int(time.time())
+ monitor_db.action('UPDATE sessions SET stopped = ?, state = ? '
+ 'WHERE session_key = ? AND rating_key = ?',
+ [stream['stopped'], 'stopped', stream['session_key'], stream['rating_key']])
+
+ # Check if the user has reached the offset in the media we defined as the "watched" percent
+ if stream['view_offset'] and stream['duration']:
+ if helpers.get_percent(stream['view_offset'],
+ stream['duration']) > plexpy.CONFIG.NOTIFY_WATCHED_PERCENT:
+ # Check if any notification agents have notifications enabled
+ if any(d['on_watched'] for d in notifiers.available_notification_agents()):
# Push any notifications -
# Push it on it's own thread so we don't hold up our db actions
threading.Thread(target=notification_handler.notify,
kwargs=dict(stream_data=stream, notify_action='watched')).start()
- else:
- # The user has stopped playing a stream
- logger.debug(u"PlexPy Monitor :: Removing sessionKey %s ratingKey %s from session queue"
- % (stream['session_key'], stream['rating_key']))
- monitor_db.action('DELETE FROM sessions WHERE session_key = ? AND rating_key = ?',
- [stream['session_key'], stream['rating_key']])
-
- # Check if the user has reached the offset in the media we defined as the "watched" percent
- if stream['view_offset'] and stream['duration']:
- if helpers.get_percent(stream['view_offset'],
- stream['duration']) > plexpy.CONFIG.NOTIFY_WATCHED_PERCENT:
- # Push any notifications -
- # Push it on it's own thread so we don't hold up our db actions
+ # Check if any notification agents have notifications enabled
+ if any(d['on_stop'] for d in notifiers.available_notification_agents()):
+ # Push any notifications - Push it on it's own thread so we don't hold up our db actions
threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=stream, notify_action='watched')).start()
-
- # Push any notifications - Push it on it's own thread so we don't hold up our db actions
- threading.Thread(target=notification_handler.notify,
- kwargs=dict(stream_data=stream, notify_action='stop')).start()
+ kwargs=dict(stream_data=stream, notify_action='stop')).start()
# Write the item history on playback stop
- monitor_process.write_session_history(session=stream)
+ success = monitor_process.write_session_history(session=stream)
+
+ if success:
+ # If session is written to the databaase successfully, remove the session from the session table
+ logger.debug(u"PlexPy Monitor :: Removing sessionKey %s ratingKey %s from session queue"
+ % (stream['session_key'], stream['rating_key']))
+ monitor_db.action('DELETE FROM sessions WHERE session_key = ? AND rating_key = ?',
+ [stream['session_key'], stream['rating_key']])
+ else:
+ logger.warn(u"PlexPy Monitor :: Failed to write sessionKey %s ratingKey %s to the database. " \
+ "Will try again on the next pass." % (stream['session_key'], stream['rating_key']))
# Process the newly received session data
for session in media_container:
- monitor_process.write_session(session)
+ new_session = monitor_process.write_session(session)
+
+ if new_session:
+ logger.debug(u"PlexPy Monitor :: Session %s has started." % session['session_key'])
+
else:
logger.debug(u"PlexPy Monitor :: Unable to read session list.")
@@ -171,9 +215,11 @@ def check_active_sessions(ws_request=False):
% 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()
+ # Check if any notification agents have notifications enabled
+ if any(d['on_intdown'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify_timeline,
+ kwargs=dict(notify_action='intdown')).start()
def check_recently_added():
@@ -225,9 +271,12 @@ def check_recently_added():
if 0 < time_threshold - int(item['added_at']) <= time_interval:
logger.debug(u"PlexPy Monitor :: Library item %s has been added to Plex." % str(item['rating_key']))
- # Fire off notifications
- threading.Thread(target=notification_handler.notify_timeline,
- kwargs=dict(timeline_data=item, notify_action='created')).start()
+
+ # Check if any notification agents have notifications enabled
+ if any(d['on_created'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify_timeline,
+ kwargs=dict(timeline_data=item, notify_action='created')).start()
else:
item = max(metadata, key=lambda x:x['added_at'])
@@ -243,9 +292,12 @@ def check_recently_added():
% str(item['rating_key']))
logger.debug(u"PlexPy Monitor :: Library item %s has been added to Plex." % str(item['rating_key']))
- # Fire off notifications
- threading.Thread(target=notification_handler.notify_timeline,
- kwargs=dict(timeline_data=item, notify_action='created')).start()
+
+ # Check if any notification agents have notifications enabled
+ if any(d['on_created'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify_timeline,
+ kwargs=dict(timeline_data=item, notify_action='created')).start()
def check_server_response():
@@ -275,12 +327,44 @@ def check_server_response():
else:
if ext_ping_count >= 3:
logger.info(u"PlexPy Monitor :: Plex remote access is back up.")
- # Fire off notifications
- threading.Thread(target=notification_handler.notify_timeline,
- kwargs=dict(notify_action='extup')).start()
+
+ # Check if any notification agents have notifications enabled
+ if any(d['on_extup'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify_timeline,
+ kwargs=dict(notify_action='extup')).start()
ext_ping_count = 0
if ext_ping_count == 3:
- # Fire off notifications
- threading.Thread(target=notification_handler.notify_timeline,
- kwargs=dict(notify_action='extdown')).start()
\ No newline at end of file
+ # Check if any notification agents have notifications enabled
+ if any(d['on_extdown'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify_timeline,
+ kwargs=dict(notify_action='extdown')).start()
+
+
+def check_server_updates():
+
+ with monitor_lock:
+ logger.info(u"PlexPy Monitor :: Checking for PMS updates...")
+
+ pms_connect = pmsconnect.PmsConnect()
+
+ server_identity = pms_connect.get_server_identity()
+ update_status = pms_connect.get_update_staus()
+
+ if server_identity and update_status:
+ version = server_identity['version']
+ logger.info(u"PlexPy Monitor :: Current PMS version: %s", version)
+
+ if update_status['state'] == 'available':
+ update_version = update_status['version']
+ logger.info(u"PlexPy Monitor :: PMS update available version: %s", update_version)
+
+ # Check if any notification agents have notifications enabled
+ if any(d['on_pmsupdate'] for d in notifiers.available_notification_agents()):
+ # Fire off notifications
+ threading.Thread(target=notification_handler.notify_timeline,
+ kwargs=dict(notify_action='pmsupdate')).start()
+ else:
+ logger.info(u"PlexPy Monitor :: No PMS update available.")
\ No newline at end of file
diff --git a/plexpy/activity_processor.py b/plexpy/activity_processor.py
index 0ea6e532..b6fdad22 100644
--- a/plexpy/activity_processor.py
+++ b/plexpy/activity_processor.py
@@ -13,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, pmsconnect, notification_handler, log_reader, database
+from plexpy import logger, pmsconnect, notification_handler, log_reader, database, notifiers
import threading
import plexpy
@@ -78,9 +78,10 @@ class ActivityProcessor(object):
result = self.db.upsert('sessions', values, keys)
if result == 'insert':
- # Push any notifications - Push it on it's own thread so we don't hold up our db actions
- if notify:
+ # Check if any notification agents have notifications enabled
+ if notify and any(d['on_play'] for d in notifiers.available_notification_agents()):
values.update({'ip_address': session['ip_address']})
+ # Push any notifications - Push it on it's own thread so we don't hold up our db actions
threading.Thread(target=notification_handler.notify,
kwargs=dict(stream_data=values, notify_action='play')).start()
@@ -97,16 +98,23 @@ class ActivityProcessor(object):
ip_address = {'ip_address': ip_address}
self.db.upsert('sessions', ip_address, keys)
+ return True
+
def write_session_history(self, session=None, import_metadata=None, is_import=False, import_ignore_interval=0):
from plexpy import users, libraries
- user_data = users.Users()
- user_details = user_data.get_details(user_id=session['user_id'])
-
section_id = session['section_id'] if not is_import else import_metadata['section_id']
- library_data = libraries.Libraries()
- library_details = library_data.get_details(section_id=section_id)
+ if not is_import:
+ user_data = users.Users()
+ user_details = user_data.get_details(user_id=session['user_id'])
+
+ library_data = libraries.Libraries()
+ library_details = library_data.get_details(section_id=section_id)
+
+ # Return false if failed to retrieve user or library details
+ if not user_details or not library_details:
+ return False
if session:
logging_enabled = False
@@ -116,8 +124,14 @@ class ActivityProcessor(object):
stopped = int(session['stopped'])
else:
stopped = int(time.time())
+ elif session['stopped']:
+ stopped = int(session['stopped'])
else:
stopped = int(time.time())
+ self.set_session_state(session_key=session['session_key'],
+ state='stopped',
+ view_offset=session['viewOffset'],
+ stopped=stopped)
if plexpy.CONFIG.MOVIE_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \
session['media_type'] == 'movie':
@@ -137,14 +151,14 @@ class ActivityProcessor(object):
else:
real_play_time = stopped - session['started']
- if plexpy.CONFIG.LOGGING_IGNORE_INTERVAL and not is_import:
+ if not is_import and plexpy.CONFIG.LOGGING_IGNORE_INTERVAL:
if (session['media_type'] == 'movie' or session['media_type'] == 'episode') and \
(real_play_time < int(plexpy.CONFIG.LOGGING_IGNORE_INTERVAL)):
logging_enabled = False
logger.debug(u"PlexPy ActivityProcessor :: Play duration for ratingKey %s is %s secs which is less than %s "
u"seconds, so we're not logging it." %
(session['rating_key'], str(real_play_time), plexpy.CONFIG.LOGGING_IGNORE_INTERVAL))
- if session['media_type'] == 'track' and not is_import:
+ if not is_import and session['media_type'] == 'track':
if real_play_time < 15 and session['duration'] >= 30:
logging_enabled = False
logger.debug(u"PlexPy ActivityProcessor :: Play duration for ratingKey %s is %s secs, "
@@ -156,17 +170,29 @@ class ActivityProcessor(object):
logging_enabled = False
logger.debug(u"PlexPy ActivityProcessor :: Play duration for ratingKey %s is %s secs which is less than %s "
u"seconds, so we're not logging it." %
- (session['rating_key'], str(real_play_time),
- import_ignore_interval))
+ (session['rating_key'], str(real_play_time), import_ignore_interval))
- if not user_details['keep_history'] and not is_import:
+ if not is_import and not user_details['keep_history']:
logging_enabled = False
logger.debug(u"PlexPy ActivityProcessor :: History logging for user '%s' is disabled." % user_details['username'])
- elif not library_details['keep_history'] and not is_import:
+ elif not is_import and not library_details['keep_history']:
logging_enabled = False
logger.debug(u"PlexPy ActivityProcessor :: History logging for library '%s' is disabled." % library_details['section_name'])
if logging_enabled:
+
+ # Fetch metadata first so we can return false if it fails
+ if not is_import:
+ logger.debug(u"PlexPy ActivityProcessor :: Fetching metadata for item ratingKey %s" % session['rating_key'])
+ pms_connect = pmsconnect.PmsConnect()
+ result = pms_connect.get_metadata_details(rating_key=str(session['rating_key']))
+ if result:
+ metadata = result['metadata']
+ else:
+ return False
+ else:
+ metadata = import_metadata
+
# logger.debug(u"PlexPy ActivityProcessor :: Attempting to write to session_history table...")
query = 'INSERT INTO session_history (started, stopped, rating_key, parent_rating_key, ' \
'grandparent_rating_key, media_type, user_id, user, ip_address, paused_counter, player, ' \
@@ -218,13 +244,22 @@ class ActivityProcessor(object):
# % last_id)
# Write the session_history_media_info table
+
+ # Generate a combined transcode decision value
+ if session['video_decision'] == 'transcode' or session['audio_decision'] == 'transcode':
+ transcode_decision = 'transcode'
+ elif session['video_decision'] == 'copy' or session['audio_decision'] == 'copy':
+ transcode_decision = 'copy'
+ else:
+ transcode_decision = 'direct play'
+
# logger.debug(u"PlexPy ActivityProcessor :: Attempting to write to session_history_media_info table...")
query = 'INSERT INTO session_history_media_info (id, rating_key, video_decision, audio_decision, ' \
'duration, width, height, container, video_codec, audio_codec, bitrate, video_resolution, ' \
'video_framerate, aspect_ratio, audio_channels, transcode_protocol, transcode_container, ' \
'transcode_video_codec, transcode_audio_codec, transcode_audio_channels, transcode_width, ' \
- 'transcode_height) VALUES ' \
- '(last_insert_rowid(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
+ 'transcode_height, transcode_decision) VALUES ' \
+ '(last_insert_rowid(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
args = [session['rating_key'], session['video_decision'], session['audio_decision'],
session['duration'], session['width'], session['height'], session['container'],
@@ -232,19 +267,12 @@ class ActivityProcessor(object):
session['video_resolution'], session['video_framerate'], session['aspect_ratio'],
session['audio_channels'], session['transcode_protocol'], session['transcode_container'],
session['transcode_video_codec'], session['transcode_audio_codec'],
- session['transcode_audio_channels'], session['transcode_width'], session['transcode_height']]
+ session['transcode_audio_channels'], session['transcode_width'], session['transcode_height'],
+ transcode_decision]
# logger.debug(u"PlexPy ActivityProcessor :: Writing session_history_media_info transaction...")
self.db.action(query=query, args=args)
- if not is_import:
- logger.debug(u"PlexPy ActivityProcessor :: Fetching metadata for item ratingKey %s" % session['rating_key'])
- pms_connect = pmsconnect.PmsConnect()
- result = pms_connect.get_metadata_details(rating_key=str(session['rating_key']))
- metadata = result['metadata']
- else:
- metadata = import_metadata
-
# Write the session_history_metadata table
directors = ";".join(metadata['directors'])
writers = ";".join(metadata['writers'])
@@ -280,6 +308,9 @@ class ActivityProcessor(object):
# logger.debug(u"PlexPy ActivityProcessor :: Writing session_history_metadata transaction...")
self.db.action(query=query, args=args)
+ # Return true when the session is successfully written to the database
+ return True
+
def find_session_ip(self, rating_key=None, machine_id=None):
logger.debug(u"PlexPy ActivityProcessor :: Requesting log lines...")
@@ -351,12 +382,15 @@ class ActivityProcessor(object):
return None
- def set_session_state(self, session_key=None, state=None, view_offset=0):
+ def set_session_state(self, session_key=None, state=None, view_offset=0, **kwargs):
if str(session_key).isdigit() and str(view_offset).isdigit():
values = {'view_offset': int(view_offset)}
if state:
values['state'] = state
+ for k,v in kwargs.iteritems():
+ values[k] = v
+
keys = {'session_key': session_key}
result = self.db.upsert('sessions', values, keys)
diff --git a/plexpy/common.py b/plexpy/common.py
index 22716140..9dce4283 100644
--- a/plexpy/common.py
+++ b/plexpy/common.py
@@ -62,6 +62,7 @@ MEDIA_FLAGS_VIDEO = {'avc1': 'h264',
SCHEDULER_LIST = ['Check GitHub for updates',
'Check for active sessions',
'Check for recently added items',
+ 'Check for Plex updates',
'Check for Plex remote access',
'Refresh users list',
'Refresh libraries list',
diff --git a/plexpy/config.py b/plexpy/config.py
index ae668035..ccb05c6d 100644
--- a/plexpy/config.py
+++ b/plexpy/config.py
@@ -48,6 +48,7 @@ _CONFIG_DEFINITIONS = {
'BOXCAR_ON_INTDOWN': (int, 'Boxcar', 0),
'BOXCAR_ON_EXTUP': (int, 'Boxcar', 0),
'BOXCAR_ON_INTUP': (int, 'Boxcar', 0),
+ 'BOXCAR_ON_PMSUPDATE': (int, 'Boxcar', 0),
'BUFFER_THRESHOLD': (int, 'Monitoring', 3),
'BUFFER_WAIT': (int, 'Monitoring', 900),
'BACKUP_DIR': (str, 'General', ''),
@@ -81,6 +82,7 @@ _CONFIG_DEFINITIONS = {
'EMAIL_ON_INTDOWN': (int, 'Email', 0),
'EMAIL_ON_EXTUP': (int, 'Email', 0),
'EMAIL_ON_INTUP': (int, 'Email', 0),
+ 'EMAIL_ON_PMSUPDATE': (int, 'Email', 0),
'ENABLE_HTTPS': (int, 'General', 0),
'FACEBOOK_ENABLED': (int, 'Facebook', 0),
'FACEBOOK_REDIRECT_URI': (str, 'Facebook', ''),
@@ -88,6 +90,7 @@ _CONFIG_DEFINITIONS = {
'FACEBOOK_APP_SECRET': (str, 'Facebook', ''),
'FACEBOOK_TOKEN': (str, 'Facebook', ''),
'FACEBOOK_GROUP': (str, 'Facebook', ''),
+ 'FACEBOOK_INCL_PMSLINK': (int, 'Facebook', 0),
'FACEBOOK_INCL_POSTER': (int, 'Facebook', 1),
'FACEBOOK_INCL_SUBJECT': (int, 'Facebook', 1),
'FACEBOOK_ON_PLAY': (int, 'Facebook', 0),
@@ -101,6 +104,7 @@ _CONFIG_DEFINITIONS = {
'FACEBOOK_ON_INTDOWN': (int, 'Facebook', 0),
'FACEBOOK_ON_EXTUP': (int, 'Facebook', 0),
'FACEBOOK_ON_INTUP': (int, 'Facebook', 0),
+ 'FACEBOOK_ON_PMSUPDATE': (int, 'Facebook', 0),
'FIRST_RUN_COMPLETE': (int, 'General', 0),
'FREEZE_DB': (int, 'General', 0),
'GET_FILE_SIZES': (int, 'General', 0),
@@ -126,6 +130,7 @@ _CONFIG_DEFINITIONS = {
'GROWL_ON_INTDOWN': (int, 'Growl', 0),
'GROWL_ON_EXTUP': (int, 'Growl', 0),
'GROWL_ON_INTUP': (int, 'Growl', 0),
+ 'GROWL_ON_PMSUPDATE': (int, 'Growl', 0),
'HOME_LIBRARY_CARDS': (list, 'General', ['first_run']),
'HOME_STATS_LENGTH': (int, 'General', 30),
'HOME_STATS_TYPE': (int, 'General', 0),
@@ -159,6 +164,7 @@ _CONFIG_DEFINITIONS = {
'IFTTT_ON_INTDOWN': (int, 'IFTTT', 0),
'IFTTT_ON_EXTUP': (int, 'IFTTT', 0),
'IFTTT_ON_INTUP': (int, 'IFTTT', 0),
+ 'IFTTT_ON_PMSUPDATE': (int, 'IFTTT', 0),
'JOURNAL_MODE': (str, 'Advanced', 'wal'),
'LAUNCH_BROWSER': (int, 'General', 1),
'LOG_DIR': (str, 'General', ''),
@@ -173,6 +179,7 @@ _CONFIG_DEFINITIONS = {
'MUSIC_NOTIFY_ON_START': (int, 'Monitoring', 1),
'MUSIC_NOTIFY_ON_STOP': (int, 'Monitoring', 0),
'MUSIC_NOTIFY_ON_PAUSE': (int, 'Monitoring', 0),
+ 'MONITOR_PMS_UPDATES': (int, 'Monitoring', 0),
'MONITOR_REMOTE_ACCESS': (int, 'Monitoring', 0),
'MONITORING_INTERVAL': (int, 'Monitoring', 60),
'MONITORING_USE_WEBSOCKET': (int, 'Monitoring', 0),
@@ -190,6 +197,7 @@ _CONFIG_DEFINITIONS = {
'NMA_ON_INTDOWN': (int, 'NMA', 0),
'NMA_ON_EXTUP': (int, 'NMA', 0),
'NMA_ON_INTUP': (int, 'NMA', 0),
+ 'NMA_ON_PMSUPDATE': (int, 'NMA', 0),
'NOTIFY_CONSECUTIVE': (int, 'Monitoring', 1),
'NOTIFY_UPLOAD_POSTERS': (int, 'Monitoring', 0),
'NOTIFY_RECENTLY_ADDED': (int, 'Monitoring', 0),
@@ -218,6 +226,8 @@ _CONFIG_DEFINITIONS = {
'NOTIFY_ON_EXTUP_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server remote access is back up.'),
'NOTIFY_ON_INTUP_SUBJECT_TEXT': (unicode, 'Monitoring', 'PlexPy ({server_name})'),
'NOTIFY_ON_INTUP_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server is back up.'),
+ 'NOTIFY_ON_PMSUPDATE_SUBJECT_TEXT': (unicode, 'Monitoring', 'PlexPy ({server_name})'),
+ 'NOTIFY_ON_PMSUPDATE_BODY_TEXT': (unicode, 'Monitoring', 'An update is available for the Plex Media Server (version {update_version}).'),
'NOTIFY_SCRIPTS_ARGS_TEXT': (unicode, 'Monitoring', ''),
'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/PlexPy'),
'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0),
@@ -232,6 +242,7 @@ _CONFIG_DEFINITIONS = {
'OSX_NOTIFY_ON_INTDOWN': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_EXTUP': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_INTUP': (int, 'OSX_Notify', 0),
+ 'OSX_NOTIFY_ON_PMSUPDATE': (int, 'OSX_Notify', 0),
'PLEX_CLIENT_HOST': (str, 'Plex', ''),
'PLEX_ENABLED': (int, 'Plex', 0),
'PLEX_PASSWORD': (str, 'Plex', ''),
@@ -247,6 +258,7 @@ _CONFIG_DEFINITIONS = {
'PLEX_ON_INTDOWN': (int, 'Plex', 0),
'PLEX_ON_EXTUP': (int, 'Plex', 0),
'PLEX_ON_INTUP': (int, 'Plex', 0),
+ 'PLEX_ON_PMSUPDATE': (int, 'Plex', 0),
'PROWL_ENABLED': (int, 'Prowl', 0),
'PROWL_KEYS': (str, 'Prowl', ''),
'PROWL_PRIORITY': (int, 'Prowl', 0),
@@ -261,6 +273,7 @@ _CONFIG_DEFINITIONS = {
'PROWL_ON_INTDOWN': (int, 'Prowl', 0),
'PROWL_ON_EXTUP': (int, 'Prowl', 0),
'PROWL_ON_INTUP': (int, 'Prowl', 0),
+ 'PROWL_ON_PMSUPDATE': (int, 'Prowl', 0),
'PUSHALOT_APIKEY': (str, 'Pushalot', ''),
'PUSHALOT_ENABLED': (int, 'Pushalot', 0),
'PUSHALOT_ON_PLAY': (int, 'Pushalot', 0),
@@ -274,6 +287,7 @@ _CONFIG_DEFINITIONS = {
'PUSHALOT_ON_INTDOWN': (int, 'Pushalot', 0),
'PUSHALOT_ON_EXTUP': (int, 'Pushalot', 0),
'PUSHALOT_ON_INTUP': (int, 'Pushalot', 0),
+ 'PUSHALOT_ON_PMSUPDATE': (int, 'Pushalot', 0),
'PUSHBULLET_APIKEY': (str, 'PushBullet', ''),
'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''),
'PUSHBULLET_CHANNEL_TAG': (str, 'PushBullet', ''),
@@ -289,6 +303,7 @@ _CONFIG_DEFINITIONS = {
'PUSHBULLET_ON_INTDOWN': (int, 'PushBullet', 0),
'PUSHBULLET_ON_EXTUP': (int, 'PushBullet', 0),
'PUSHBULLET_ON_INTUP': (int, 'PushBullet', 0),
+ 'PUSHBULLET_ON_PMSUPDATE': (int, 'PushBullet', 0),
'PUSHOVER_APITOKEN': (str, 'Pushover', ''),
'PUSHOVER_ENABLED': (int, 'Pushover', 0),
'PUSHOVER_HTML_SUPPORT': (int, 'Pushover', 1),
@@ -306,6 +321,7 @@ _CONFIG_DEFINITIONS = {
'PUSHOVER_ON_INTDOWN': (int, 'Pushover', 0),
'PUSHOVER_ON_EXTUP': (int, 'Pushover', 0),
'PUSHOVER_ON_INTUP': (int, 'Pushover', 0),
+ 'PUSHOVER_ON_PMSUPDATE': (int, 'Pushover', 0),
'REFRESH_LIBRARIES_INTERVAL': (int, 'Monitoring', 12),
'REFRESH_LIBRARIES_ON_STARTUP': (int, 'Monitoring', 1),
'REFRESH_USERS_INTERVAL': (int, 'Monitoring', 12),
@@ -327,6 +343,7 @@ _CONFIG_DEFINITIONS = {
'SLACK_ON_INTDOWN': (int, 'Slack', 0),
'SLACK_ON_EXTUP': (int, 'Slack', 0),
'SLACK_ON_INTUP': (int, 'Slack', 0),
+ 'SLACK_ON_PMSUPDATE': (int, 'Slack', 0),
'SCRIPTS_ENABLED': (int, 'Scripts', 0),
'SCRIPTS_FOLDER': (unicode, 'Scripts', ''),
'SCRIPTS_ON_PLAY': (int, 'Scripts', 0),
@@ -340,6 +357,7 @@ _CONFIG_DEFINITIONS = {
'SCRIPTS_ON_EXTUP': (int, 'Scripts', 0),
'SCRIPTS_ON_INTDOWN': (int, 'Scripts', 0),
'SCRIPTS_ON_INTUP': (int, 'Scripts', 0),
+ 'SCRIPTS_ON_PMSUPDATE': (int, 'Scripts', 0),
'SCRIPTS_ON_PLAY_SCRIPT': (unicode, 'Scripts', ''),
'SCRIPTS_ON_STOP_SCRIPT': (unicode, 'Scripts', ''),
'SCRIPTS_ON_PAUSE_SCRIPT': (unicode, 'Scripts', ''),
@@ -351,6 +369,7 @@ _CONFIG_DEFINITIONS = {
'SCRIPTS_ON_EXTUP_SCRIPT': (unicode, 'Scripts', ''),
'SCRIPTS_ON_INTDOWN_SCRIPT': (unicode, 'Scripts', ''),
'SCRIPTS_ON_INTUP_SCRIPT': (unicode, 'Scripts', ''),
+ 'SCRIPTS_ON_PMSUPDATE_SCRIPT': (unicode, 'Scripts', ''),
'TELEGRAM_BOT_TOKEN': (str, 'Telegram', ''),
'TELEGRAM_ENABLED': (int, 'Telegram', 0),
'TELEGRAM_CHAT_ID': (str, 'Telegram', ''),
@@ -366,6 +385,7 @@ _CONFIG_DEFINITIONS = {
'TELEGRAM_ON_INTDOWN': (int, 'Telegram', 0),
'TELEGRAM_ON_EXTUP': (int, 'Telegram', 0),
'TELEGRAM_ON_INTUP': (int, 'Telegram', 0),
+ 'TELEGRAM_ON_PMSUPDATE': (int, 'Telegram', 0),
'TV_LOGGING_ENABLE': (int, 'Monitoring', 1),
'TV_NOTIFY_ENABLE': (int, 'Monitoring', 0),
'TV_NOTIFY_ON_START': (int, 'Monitoring', 1),
@@ -388,6 +408,7 @@ _CONFIG_DEFINITIONS = {
'TWITTER_ON_INTDOWN': (int, 'Twitter', 0),
'TWITTER_ON_EXTUP': (int, 'Twitter', 0),
'TWITTER_ON_INTUP': (int, 'Twitter', 0),
+ 'TWITTER_ON_PMSUPDATE': (int, 'Twitter', 0),
'UPDATE_DB_INTERVAL': (int, 'General', 24),
'UPDATE_SECTION_IDS': (int, 'General', 1),
'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1),
@@ -406,7 +427,8 @@ _CONFIG_DEFINITIONS = {
'XBMC_ON_EXTDOWN': (int, 'XBMC', 0),
'XBMC_ON_INTDOWN': (int, 'XBMC', 0),
'XBMC_ON_EXTUP': (int, 'XBMC', 0),
- 'XBMC_ON_INTUP': (int, 'XBMC', 0)
+ 'XBMC_ON_INTUP': (int, 'XBMC', 0),
+ 'XBMC_ON_PMSUPDATE': (int, 'XBMC', 0)
}
diff --git a/plexpy/database.py b/plexpy/database.py
index 62d81363..6dca1ac2 100644
--- a/plexpy/database.py
+++ b/plexpy/database.py
@@ -18,6 +18,7 @@ import os
import sqlite3
import shutil
import threading
+import time
import logger
import plexpy
@@ -37,9 +38,21 @@ def clear_history_tables():
monitor_db.action('DELETE FROM session_history')
monitor_db.action('DELETE FROM session_history_media_info')
monitor_db.action('DELETE FROM session_history_metadata')
- monitor_db.action('VACUUM;')
+ monitor_db.action('VACUUM')
+def delete_sessions():
+ logger.debug(u"PlexPy Database :: Clearing temporary sessions from database.")
+ monitor_db = MonitorDatabase()
+
+ try:
+ monitor_db.action('DELETE FROM sessions')
+ monitor_db.action('VACUUM')
+ return 'Cleared temporary sessions.'
+ except Exception as e:
+ logger.warn(u"PlexPy Database :: Unable to clear temporary sessions from database: %s." % e)
+ return 'Unable to clear temporary sessions.'
+
def db_filename(filename="plexpy.db"):
""" Returns the filepath to the db """
diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py
index 2857417e..2900e8f8 100644
--- a/plexpy/datafactory.py
+++ b/plexpy/datafactory.py
@@ -58,11 +58,10 @@ class DataFactory(object):
'session_history_metadata.thumb',
'session_history_metadata.parent_thumb',
'session_history_metadata.grandparent_thumb',
- 'MAX((CASE WHEN view_offset IS NULL THEN 0.1 ELSE view_offset * 1.0 END) / \
- (CASE WHEN session_history_metadata.duration IS NULL THEN 1.0 \
- ELSE session_history_metadata.duration * 1.0 END) * 100) AS percent_complete',
- 'session_history_media_info.video_decision',
- 'session_history_media_info.audio_decision',
+ 'MAX((CASE WHEN (view_offset IS NULL OR view_offset = "") THEN 0.1 ELSE view_offset * 1.0 END) / \
+ (CASE WHEN (session_history_metadata.duration IS NULL OR session_history_metadata.duration = "") \
+ THEN 1.0 ELSE session_history_metadata.duration * 1.0 END) * 100) AS percent_complete',
+ 'session_history_media_info.transcode_decision',
'COUNT(*) AS group_count',
'GROUP_CONCAT(session_history.id) AS group_ids'
]
@@ -138,8 +137,7 @@ class DataFactory(object):
'media_index': item['media_index'],
'parent_media_index': item['parent_media_index'],
'thumb': thumb,
- 'video_decision': item['video_decision'],
- 'audio_decision': item['audio_decision'],
+ 'transcode_decision': item['transcode_decision'],
'percent_complete': int(round(item['percent_complete'])),
'watched_status': watched_status,
'group_count': item['group_count'],
@@ -626,24 +624,21 @@ class DataFactory(object):
title = 'Concurrent Transcodes'
query = base_query \
- + 'AND (session_history_media_info.video_decision = "transcode" ' \
- 'OR session_history_media_info.audio_decision = "transcode") '
+ + 'AND session_history_media_info.transcode_decision = "transcode" '
result = monitor_db.select(query)
if result:
most_concurrent.append(calc_most_concurrent(title, result))
title = 'Concurrent Direct Streams'
query = base_query \
- + 'AND (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") '
+ + 'AND session_history_media_info.transcode_decision = "copy" '
result = monitor_db.select(query)
if result:
most_concurrent.append(calc_most_concurrent(title, result))
title = 'Concurrent Direct Plays'
query = base_query \
- + 'AND (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") '
+ + 'AND session_history_media_info.transcode_decision = "direct play" '
result = monitor_db.select(query)
if result:
most_concurrent.append(calc_most_concurrent(title, result))
@@ -828,6 +823,7 @@ class DataFactory(object):
'SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS total_duration ' \
'FROM session_history ' \
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
+ 'JOIN session_history_media_info ON session_history_media_info.id = session_history.id ' \
'%s ' % where
result = monitor_db.select(query)
except Exception as e:
@@ -860,16 +856,28 @@ class DataFactory(object):
return ip_address
- def get_poster_url(self, rating_key=''):
+ def get_poster_url(self, rating_key='', metadata=None):
monitor_db = database.MonitorDatabase()
poster_url = ''
+ poster_key = ''
if rating_key:
+ poster_key = rating_key
+ elif metadata:
+ if metadata['media_type'] == 'movie' or metadata['media_type'] == 'show' or \
+ metadata['media_type'] == 'artist' or metadata['media_type'] == 'album':
+ poster_key = metadata['rating_key']
+ elif metadata['media_type'] == 'episode':
+ poster_key = metadata['grandparent_rating_key']
+ elif metadata['media_type'] == 'season' or metadata['media_type'] == 'track':
+ poster_key = metadata['parent_rating_key']
+
+ if poster_key:
try:
query = 'SELECT id, poster_url FROM notify_log ' \
'WHERE rating_key = %d OR parent_rating_key = %d OR grandparent_rating_key = %d ' \
- 'ORDER BY id DESC LIMIT 1' % (int(rating_key), int(rating_key), int(rating_key))
+ 'ORDER BY id DESC LIMIT 1' % (int(poster_key), int(poster_key), int(poster_key))
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_poster_url: %s." % e)
@@ -882,6 +890,16 @@ class DataFactory(object):
return poster_url
+ def delete_poster_url(self, poster_url=''):
+ monitor_db = database.MonitorDatabase()
+
+ if poster_url:
+ logger.info(u"PlexPy DataFactory :: Deleting poster_url %s from the notify log database." % poster_url)
+ monitor_db.upsert('notify_log', {'poster_url': None}, {'poster_url': poster_url})
+ return 'Deleted poster_url %s.' % poster_url
+ else:
+ return 'Unable to delete poster_url.'
+
def get_search_query(self, rating_key=''):
monitor_db = database.MonitorDatabase()
@@ -1188,4 +1206,16 @@ class DataFactory(object):
'draw': query['draw']
}
- return dict
\ No newline at end of file
+ return dict
+
+ def delete_notification_log(self):
+ monitor_db = database.MonitorDatabase()
+
+ try:
+ logger.info(u"PlexPy DataFactory :: Clearing notification logs from database.")
+ monitor_db.action('DELETE FROM notify_log')
+ monitor_db.action('VACUUM')
+ return 'Cleared notification logs.'
+ except Exception as e:
+ logger.warn(u"PlexPy DataFactory :: Unable to execute database query for delete_notification_log: %s." % e)
+ return 'Unable to clear notification logs.'
\ No newline at end of file
diff --git a/plexpy/graphs.py b/plexpy/graphs.py
index e5d93720..d9f9cd55 100644
--- a/plexpy/graphs.py
+++ b/plexpy/graphs.py
@@ -490,40 +490,39 @@ class Graphs(object):
try:
if y_axis == 'plays':
query = 'SELECT date(session_history.started, "unixepoch", "localtime") AS date_played, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
- 'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
+ 'THEN 1 ELSE 0 END) AS dp_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
+ 'THEN 1 ELSE 0 END) AS ds_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
+ 'THEN 1 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") >= ' \
'datetime("now", "-%s days", "localtime")) AND ' \
- '(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
+ '(session_history.media_type = "episode" OR session_history.media_type = "movie" OR ' \
+ 'session_history.media_type = "track") ' \
'GROUP BY date_played ' \
'ORDER BY started ASC' % time_range
result = monitor_db.select(query)
else:
query = 'SELECT date(session_history.started, "unixepoch", "localtime") AS date_played, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
- 'OR session_history_media_info.audio_decision = "transcode") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_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") >= ' \
'datetime("now", "-%s days", "localtime") AND ' \
- '(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
+ '(session_history.media_type = "episode" OR session_history.media_type = "movie" OR ' \
+ 'session_history.media_type = "track") ' \
'GROUP BY date_played ' \
'ORDER BY started ASC' % time_range
@@ -583,12 +582,12 @@ class Graphs(object):
try:
if y_axis == 'plays':
query = 'SELECT session_history_media_info.video_resolution AS resolution, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
- 'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
+ 'THEN 1 ELSE 0 END) AS dp_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
+ 'THEN 1 ELSE 0 END) AS ds_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
+ 'THEN 1 ELSE 0 END) AS tc_count, ' \
'COUNT(session_history.id) AS total_count ' \
'FROM session_history ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
@@ -602,16 +601,13 @@ class Graphs(object):
result = monitor_db.select(query)
else:
query = 'SELECT session_history_media_info.video_resolution AS resolution,' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
- 'OR session_history_media_info.audio_decision = "transcode") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
'SUM(CASE WHEN stopped > 0 THEN (stopped - started) ' \
@@ -671,12 +667,12 @@ class Graphs(object):
'WHEN session_history_media_info.transcode_height <= 1440 THEN "QHD" ' \
'WHEN session_history_media_info.transcode_height <= 2160 THEN "4K" ' \
'ELSE "unknown" END) ELSE session_history_media_info.video_resolution END) AS resolution, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" '\
- 'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
+ 'THEN 1 ELSE 0 END) AS dp_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
+ 'THEN 1 ELSE 0 END) AS ds_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" '\
+ 'THEN 1 ELSE 0 END) AS tc_count, ' \
'COUNT(session_history.id) AS total_count ' \
'FROM session_history ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
@@ -700,16 +696,13 @@ class Graphs(object):
'WHEN session_history_media_info.transcode_height <= 1440 THEN "QHD" ' \
'WHEN session_history_media_info.transcode_height <= 2160 THEN "4K" ' \
'ELSE "unknown" END) ELSE session_history_media_info.video_resolution END) AS resolution, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
- 'OR session_history_media_info.audio_decision = "transcode") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
'SUM(CASE WHEN stopped > 0 THEN (stopped - started) ' \
@@ -759,12 +752,12 @@ class Graphs(object):
try:
if y_axis == 'plays':
query = 'SELECT session_history.platform AS platform, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
- 'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
+ 'THEN 1 ELSE 0 END) AS dp_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
+ 'THEN 1 ELSE 0 END) AS ds_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
+ 'THEN 1 ELSE 0 END) AS tc_count, ' \
'COUNT(session_history.id) AS total_count ' \
'FROM session_history ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
@@ -777,16 +770,13 @@ class Graphs(object):
result = monitor_db.select(query)
else:
query = 'SELECT session_history.platform AS platform, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
- 'AND session_history_media_info.audio_decision = "transcode") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
'SUM(CASE WHEN session_history.stopped > 0 ' \
@@ -838,12 +828,12 @@ class Graphs(object):
if y_axis == 'plays':
query = 'SELECT ' \
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) AS username, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
- 'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
+ 'THEN 1 ELSE 0 END) AS dp_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
+ 'THEN 1 ELSE 0 END) AS ds_count, ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
+ 'THEN 1 ELSE 0 END) AS tc_count, ' \
'COUNT(session_history.id) AS total_count ' \
'FROM session_history ' \
'JOIN users ON session_history.user_id = users.user_id ' \
@@ -858,16 +848,13 @@ class Graphs(object):
else:
query = 'SELECT ' \
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) AS username, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
- 'OR session_history_media_info.audio_decision = "direct play") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
- 'AND session_history_media_info.audio_decision = "copy") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
- 'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
- 'AND session_history_media_info.audio_decision = "transcode") ' \
+ 'SUM(CASE WHEN session_history_media_info.transcode_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_count, ' \
'SUM(CASE WHEN session_history.stopped > 0 ' \
diff --git a/plexpy/libraries.py b/plexpy/libraries.py
index 4b6f47f5..455ca7a5 100644
--- a/plexpy/libraries.py
+++ b/plexpy/libraries.py
@@ -148,7 +148,6 @@ class Libraries(object):
'session_history_metadata.year',
'session_history_metadata.media_index',
'session_history_metadata.parent_media_index',
- 'session_history_media_info.video_decision',
'library_sections.do_notify',
'library_sections.do_notify_created',
'library_sections.keep_history'
@@ -540,7 +539,7 @@ class Libraries(object):
def get_details(self, section_id=None):
from plexpy import pmsconnect
- default_return = {'section_id': None,
+ default_return = {'section_id': 0,
'section_name': 'Local',
'section_type': '',
'library_thumb': common.DEFAULT_COVER_THUMB,
@@ -550,7 +549,7 @@ class Libraries(object):
'child_count': 0,
'do_notify': 0,
'do_notify_created': 0,
- 'keep_history': 0
+ 'keep_history': 1
}
if not section_id:
@@ -603,7 +602,8 @@ class Libraries(object):
return library_details
else:
- logger.warn(u"PlexPy Libraries :: Unable to retrieve library from local database. Requesting library list refresh.")
+ logger.warn(u"PlexPy Libraries :: Unable to retrieve library %s from database. Requesting library list refresh."
+ % section_id)
# Let's first refresh the libraries list to make sure the library isn't newly added and not in the db yet
pmsconnect.refresh_libraries()
@@ -613,9 +613,9 @@ class Libraries(object):
return library_details
else:
- logger.warn(u"PlexPy Users :: Unable to retrieve user from local database. Returning 'Local' library.")
+ logger.warn(u"PlexPy Users :: Unable to retrieve library %s from database. Returning 'Local' library."
+ % section_id)
# If there is no library data we must return something
- # Use "Local" library to retain compatibility with PlexWatch database value
return default_return
def get_watch_time_stats(self, section_id=None):
diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py
index 62c83e43..a855f305 100644
--- a/plexpy/notification_handler.py
+++ b/plexpy/notification_handler.py
@@ -50,7 +50,10 @@ def notify(stream_data=None, notify_action=None):
for agent in notifiers.available_notification_agents():
if agent['on_play'] and notify_action == 'play':
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -68,7 +71,10 @@ def notify(stream_data=None, notify_action=None):
elif agent['on_stop'] and notify_action == 'stop' \
and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < plexpy.CONFIG.NOTIFY_WATCHED_PERCENT):
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -86,7 +92,10 @@ def notify(stream_data=None, notify_action=None):
elif agent['on_pause'] and notify_action == 'pause' \
and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99):
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -104,7 +113,10 @@ def notify(stream_data=None, notify_action=None):
elif agent['on_resume'] and notify_action == 'resume' \
and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99):
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -121,7 +133,10 @@ def notify(stream_data=None, notify_action=None):
elif agent['on_buffer'] and notify_action == 'buffer':
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -143,7 +158,10 @@ def notify(stream_data=None, notify_action=None):
# If there is nothing in the notify_log for our agent id but it is enabled we should notify
if not any(d['agent_id'] == agent['id'] and d['notify_action'] == notify_action for d in notify_states):
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -163,7 +181,10 @@ def notify(stream_data=None, notify_action=None):
for agent in notifiers.available_notification_agents():
if agent['on_play'] and notify_action == 'play':
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -180,7 +201,10 @@ def notify(stream_data=None, notify_action=None):
elif agent['on_stop'] and notify_action == 'stop':
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -197,7 +221,10 @@ def notify(stream_data=None, notify_action=None):
elif agent['on_pause'] and notify_action == 'pause':
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -214,7 +241,10 @@ def notify(stream_data=None, notify_action=None):
elif agent['on_resume'] and notify_action == 'resume':
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -231,7 +261,10 @@ def notify(stream_data=None, notify_action=None):
elif agent['on_buffer'] and notify_action == 'buffer':
# Build and send notification
- notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(session=stream_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -260,7 +293,10 @@ def notify_timeline(timeline_data=None, notify_action=None):
for agent in notifiers.available_notification_agents():
if agent['on_created'] and notify_action == 'created':
# Build and send notification
- notify_strings, metadata = build_notify_text(timeline=timeline_data, notify_action=notify_action)
+ notify_strings, metadata = build_notify_text(timeline=timeline_data,
+ notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -279,7 +315,9 @@ def notify_timeline(timeline_data=None, notify_action=None):
for agent in notifiers.available_notification_agents():
if agent['on_extdown'] and notify_action == 'extdown':
# Build and send notification
- notify_strings = build_server_notify_text(notify_action=notify_action)
+ notify_strings = build_server_notify_text(notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -295,7 +333,9 @@ def notify_timeline(timeline_data=None, notify_action=None):
if agent['on_intdown'] and notify_action == 'intdown':
# Build and send notification
- notify_strings = build_server_notify_text(notify_action=notify_action)
+ notify_strings = build_server_notify_text(notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -311,7 +351,9 @@ def notify_timeline(timeline_data=None, notify_action=None):
if agent['on_extup'] and notify_action == 'extup':
# Build and send notification
- notify_strings = build_server_notify_text(notify_action=notify_action)
+ notify_strings = build_server_notify_text(notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -327,7 +369,27 @@ def notify_timeline(timeline_data=None, notify_action=None):
if agent['on_intup'] and notify_action == 'intup':
# Build and send notification
- notify_strings = build_server_notify_text(notify_action=notify_action)
+ notify_strings = build_server_notify_text(notify_action=notify_action,
+ agent_id=agent['id'])
+
+ notifiers.send_notification(agent_id=agent['id'],
+ subject=notify_strings[0],
+ body=notify_strings[1],
+ script_args=notify_strings[2],
+ notify_action=notify_action)
+
+ # Set the notification state in the db
+ set_notify_state(session={},
+ notify_action=notify_action,
+ agent_info=agent,
+ notify_strings=notify_strings,
+ metadata={})
+
+ if agent['on_pmsupdate'] and notify_action == 'pmsupdate':
+ # Build and send notification
+ notify_strings = build_server_notify_text(notify_action=notify_action,
+ agent_id=agent['id'])
+
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
@@ -395,11 +457,11 @@ def set_notify_state(session, notify_action, agent_info, notify_strings, metadat
logger.error(u"PlexPy NotificationHandler :: Unable to set notify state.")
-def build_notify_text(session=None, timeline=None, notify_action=None):
+def build_notify_text(session=None, timeline=None, notify_action=None, agent_id=None):
# Get time formats
- date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','').replace('zz','')
- time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('zz','')
- duration_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('zz','').replace('a','').replace('A','')
+ date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','')
+ time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','')
+ duration_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('a','').replace('A','')
# Get the server name
server_name = plexpy.CONFIG.PMS_NAME
@@ -408,6 +470,10 @@ def build_notify_text(session=None, timeline=None, notify_action=None):
plex_tv = plextv.PlexTV()
server_times = plex_tv.get_server_times()
+ # Get the server version
+ pms_connect = pmsconnect.PmsConnect()
+ server_identity = pms_connect.get_server_identity()
+
if server_times:
updated_at = server_times[0]['updated_at']
server_uptime = helpers.human_duration(int(time.time() - helpers.cast_to_int(updated_at)))
@@ -435,13 +501,13 @@ def build_notify_text(session=None, timeline=None, notify_action=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('\n*
[^>]+.\n*|\n*
[^>]+.\n*', re.IGNORECASE | re.DOTALL)
+ pattern = re.compile(r'
[^>]+.<\/tv>|[^>]+.<\/music>', 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('\n*[^>]+.\n*|\n*?[^>]+.\n*', re.IGNORECASE | re.DOTALL)
+ pattern = re.compile(r'[^>]+.<\/movie>|[^>]+.<\/music>', 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('\n*[^>]+.\n*|\n*[^>]+.\n*', re.IGNORECASE | re.DOTALL)
+ pattern = re.compile(r'[^>]+.<\/tv>|[^>]+.<\/movie>', re.IGNORECASE | re.DOTALL)
else:
pattern = None
@@ -450,37 +516,37 @@ def build_notify_text(session=None, timeline=None, notify_action=None):
or metadata['media_type'] == 'artist' or metadata['media_type'] == 'track' \
and pattern:
# Remove the unwanted tags and strip any unmatch tags too.
- on_start_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT))
- on_start_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT))
- on_stop_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT))
- on_stop_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_STOP_BODY_TEXT))
- on_pause_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT))
- on_pause_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT))
- on_resume_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT))
- on_resume_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT))
- on_buffer_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT))
- on_buffer_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT))
- on_watched_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT))
- on_watched_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT))
- on_created_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT))
- on_created_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT))
- script_args_text = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT))
+ on_start_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT), agent_id)
+ on_start_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT), agent_id)
+ on_stop_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT), agent_id)
+ on_stop_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_STOP_BODY_TEXT), agent_id)
+ on_pause_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT), agent_id)
+ on_pause_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT), agent_id)
+ on_resume_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT), agent_id)
+ on_resume_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT), agent_id)
+ on_buffer_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT), agent_id)
+ on_buffer_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT), agent_id)
+ on_watched_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT), agent_id)
+ on_watched_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT), agent_id)
+ on_created_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT), agent_id)
+ on_created_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT), agent_id)
+ script_args_text = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT), agent_id)
else:
- on_start_subject = plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT
- on_start_body = plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT
- on_stop_subject = plexpy.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT
- on_stop_body = plexpy.CONFIG.NOTIFY_ON_STOP_BODY_TEXT
- on_pause_subject = plexpy.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT
- on_pause_body = plexpy.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT
- on_resume_subject = plexpy.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT
- on_resume_body = plexpy.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT
- on_buffer_subject = plexpy.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT
- on_buffer_body = plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT
- on_watched_subject = plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT
- on_watched_body = plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT
- on_created_subject = plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT
- on_created_body = plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT
- script_args_text = plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT
+ on_start_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT, agent_id)
+ on_start_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT, agent_id)
+ on_stop_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT, agent_id)
+ on_stop_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_STOP_BODY_TEXT, agent_id)
+ on_pause_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT, agent_id)
+ on_pause_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT, agent_id)
+ on_resume_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT, agent_id)
+ on_resume_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT, agent_id)
+ on_buffer_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT, agent_id)
+ on_buffer_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT, agent_id)
+ on_watched_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT, agent_id)
+ on_watched_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT, agent_id)
+ on_created_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT, agent_id)
+ on_created_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT, agent_id)
+ script_args_text = strip_tag(plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT, agent_id)
# Create a title
if metadata['media_type'] == 'episode' or metadata['media_type'] == 'track':
@@ -591,6 +657,7 @@ def build_notify_text(session=None, timeline=None, notify_action=None):
available_params = {# Global paramaters
'server_name': server_name,
'server_uptime': server_uptime,
+ 'server_version': server_identity.get('version',''),
'action': notify_action.title(),
'datestamp': arrow.now().format(date_format),
'timestamp': arrow.now().format(time_format),
@@ -854,10 +921,10 @@ def build_notify_text(session=None, timeline=None, notify_action=None):
return None
-def build_server_notify_text(notify_action=None):
+def build_server_notify_text(notify_action=None, agent_id=None):
# Get time formats
- date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','').replace('zz','')
- time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('zz','')
+ date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','')
+ time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','')
# Get the server name
server_name = plexpy.CONFIG.PMS_NAME
@@ -866,6 +933,14 @@ def build_server_notify_text(notify_action=None):
plex_tv = plextv.PlexTV()
server_times = plex_tv.get_server_times()
+ # Get the server version
+ pms_connect = pmsconnect.PmsConnect()
+ server_identity = pms_connect.get_server_identity()
+
+ update_status = {}
+ if notify_action == 'pmsupdate':
+ update_status = pms_connect.get_update_staus()
+
if server_times:
updated_at = server_times[0]['updated_at']
server_uptime = helpers.human_duration(int(time.time() - helpers.cast_to_int(updated_at)))
@@ -875,22 +950,29 @@ def build_server_notify_text(notify_action=None):
pattern = re.compile('\n*[^>]+.\n*|\n*[^>]+.\n*|\n*?[^>]+.\n*', re.IGNORECASE | re.DOTALL)
- on_extdown_subject = plexpy.CONFIG.NOTIFY_ON_EXTDOWN_SUBJECT_TEXT
- on_extdown_body = plexpy.CONFIG.NOTIFY_ON_EXTDOWN_BODY_TEXT
- on_intdown_subject = plexpy.CONFIG.NOTIFY_ON_INTDOWN_SUBJECT_TEXT
- on_intdown_body = plexpy.CONFIG.NOTIFY_ON_INTDOWN_BODY_TEXT
- on_extup_subject = plexpy.CONFIG.NOTIFY_ON_EXTUP_SUBJECT_TEXT
- on_extup_body = plexpy.CONFIG.NOTIFY_ON_EXTUP_BODY_TEXT
- on_intup_subject = plexpy.CONFIG.NOTIFY_ON_INTUP_SUBJECT_TEXT
- on_intup_body = plexpy.CONFIG.NOTIFY_ON_INTUP_BODY_TEXT
- script_args_text = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT))
+ on_extdown_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_EXTDOWN_SUBJECT_TEXT, agent_id)
+ on_extdown_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_EXTDOWN_BODY_TEXT, agent_id)
+ on_intdown_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_INTDOWN_SUBJECT_TEXT, agent_id)
+ on_intdown_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_INTDOWN_BODY_TEXT, agent_id)
+ on_extup_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_EXTUP_SUBJECT_TEXT, agent_id)
+ on_extup_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_EXTUP_BODY_TEXT, agent_id)
+ on_intup_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_INTUP_SUBJECT_TEXT, agent_id)
+ on_intup_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_INTUP_BODY_TEXT, agent_id)
+ on_pmsupdate_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_PMSUPDATE_SUBJECT_TEXT, agent_id)
+ on_pmsupdate_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_PMSUPDATE_BODY_TEXT, agent_id)
+ script_args_text = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT), agent_id)
available_params = {# Global paramaters
'server_name': server_name,
'server_uptime': server_uptime,
+ 'server_version': server_identity.get('version',''),
'action': notify_action.title(),
'datestamp': arrow.now().format(date_format),
- 'timestamp': arrow.now().format(time_format)}
+ 'timestamp': arrow.now().format(time_format),
+ # Update parameters
+ 'update_version': update_status.get('version',''),
+ 'update_url': update_status.get('download_url',''),
+ 'update_changelog': update_status.get('changelog','')}
# Default text
subject_text = 'PlexPy (%s)' % server_name
@@ -996,10 +1078,39 @@ def build_server_notify_text(notify_action=None):
else:
return [subject_text, body_text, script_args]
+ elif notify_action == 'pmsupdate':
+ # Default body text
+ body_text = 'An update is available for the Plex Media Server (version {update_version}).'
+
+ if on_pmsupdate_subject and on_pmsupdate_body:
+ try:
+ subject_text = unicode(on_pmsupdate_subject).format(**available_params)
+ except LookupError, e:
+ logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
+ except:
+ logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
+
+ try:
+ body_text = unicode(on_pmsupdate_body).format(**available_params)
+ except LookupError, e:
+ logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
+ except:
+ logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
+
+ return [subject_text, body_text, script_args]
+ else:
+ return [subject_text, body_text, script_args]
+
else:
return None
-def strip_tag(data):
- p = re.compile(r'<.*?>')
+def strip_tag(data, agent_id=None):
+ # Allow tags b, i, u, a[href], font[color] for Pushover
+ if agent_id == 7:
+ p = re.compile(r'<(?!/?(b>|i>|u>)|(a\shref=\"[^\"\'\s]+\"|/a>|font\scolor=\"[^\"\'\s]+\"|/font>)).*?>',
+ re.IGNORECASE | re.DOTALL)
+ else:
+ p = re.compile(r'<.*?>', re.IGNORECASE | re.DOTALL)
+
return p.sub('', data)
\ No newline at end of file
diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py
index afcdf741..17f67142 100644
--- a/plexpy/notifiers.py
+++ b/plexpy/notifiers.py
@@ -75,7 +75,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.GROWL_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.GROWL_ON_INTDOWN,
'on_extup': plexpy.CONFIG.GROWL_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.GROWL_ON_INTUP
+ 'on_intup': plexpy.CONFIG.GROWL_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.GROWL_ON_PMSUPDATE
},
{'name': 'Prowl',
'id': AGENT_IDS['Prowl'],
@@ -92,7 +93,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.PROWL_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.PROWL_ON_INTDOWN,
'on_extup': plexpy.CONFIG.PROWL_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.PROWL_ON_INTUP
+ 'on_intup': plexpy.CONFIG.PROWL_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.PROWL_ON_PMSUPDATE
},
{'name': 'XBMC',
'id': AGENT_IDS['XBMC'],
@@ -109,7 +111,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.XBMC_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.XBMC_ON_INTDOWN,
'on_extup': plexpy.CONFIG.XBMC_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.XBMC_ON_INTUP
+ 'on_intup': plexpy.CONFIG.XBMC_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.XBMC_ON_PMSUPDATE
},
{'name': 'Plex',
'id': AGENT_IDS['Plex'],
@@ -126,7 +129,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.PLEX_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.PLEX_ON_INTDOWN,
'on_extup': plexpy.CONFIG.PLEX_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.PLEX_ON_INTUP
+ 'on_intup': plexpy.CONFIG.PLEX_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.PLEX_ON_PMSUPDATE
},
{'name': 'NotifyMyAndroid',
'id': AGENT_IDS['NMA'],
@@ -143,7 +147,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.NMA_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.NMA_ON_INTDOWN,
'on_extup': plexpy.CONFIG.NMA_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.NMA_ON_INTUP
+ 'on_intup': plexpy.CONFIG.NMA_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.NMA_ON_PMSUPDATE
},
{'name': 'Pushalot',
'id': AGENT_IDS['Pushalot'],
@@ -160,7 +165,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.PUSHALOT_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.PUSHALOT_ON_INTDOWN,
'on_extup': plexpy.CONFIG.PUSHALOT_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.PUSHALOT_ON_INTUP
+ 'on_intup': plexpy.CONFIG.PUSHALOT_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.PUSHALOT_ON_PMSUPDATE
},
{'name': 'Pushbullet',
'id': AGENT_IDS['Pushbullet'],
@@ -177,7 +183,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.PUSHBULLET_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.PUSHBULLET_ON_INTDOWN,
'on_extup': plexpy.CONFIG.PUSHBULLET_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.PUSHBULLET_ON_INTUP
+ 'on_intup': plexpy.CONFIG.PUSHBULLET_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.PUSHBULLET_ON_PMSUPDATE
},
{'name': 'Pushover',
'id': AGENT_IDS['Pushover'],
@@ -194,7 +201,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.PUSHOVER_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.PUSHOVER_ON_INTDOWN,
'on_extup': plexpy.CONFIG.PUSHOVER_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.PUSHOVER_ON_INTUP
+ 'on_intup': plexpy.CONFIG.PUSHOVER_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.PUSHOVER_ON_PMSUPDATE
},
{'name': 'Boxcar2',
'id': AGENT_IDS['Boxcar2'],
@@ -211,7 +219,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.BOXCAR_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.BOXCAR_ON_INTDOWN,
'on_extup': plexpy.CONFIG.BOXCAR_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.BOXCAR_ON_INTUP
+ 'on_intup': plexpy.CONFIG.BOXCAR_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.BOXCAR_ON_PMSUPDATE
},
{'name': 'E-mail',
'id': AGENT_IDS['Email'],
@@ -228,7 +237,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.EMAIL_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.EMAIL_ON_INTDOWN,
'on_extup': plexpy.CONFIG.EMAIL_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.EMAIL_ON_INTUP
+ 'on_intup': plexpy.CONFIG.EMAIL_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.EMAIL_ON_PMSUPDATE
},
{'name': 'Twitter',
'id': AGENT_IDS['Twitter'],
@@ -245,7 +255,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.TWITTER_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.TWITTER_ON_INTDOWN,
'on_extup': plexpy.CONFIG.TWITTER_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.TWITTER_ON_INTUP
+ 'on_intup': plexpy.CONFIG.TWITTER_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.TWITTER_ON_PMSUPDATE
},
{'name': 'IFTTT',
'id': AGENT_IDS['IFTTT'],
@@ -262,7 +273,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.IFTTT_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.IFTTT_ON_INTDOWN,
'on_extup': plexpy.CONFIG.IFTTT_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.IFTTT_ON_INTUP
+ 'on_intup': plexpy.CONFIG.IFTTT_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.IFTTT_ON_PMSUPDATE
},
{'name': 'Telegram',
'id': AGENT_IDS['Telegram'],
@@ -279,7 +291,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.TELEGRAM_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.TELEGRAM_ON_INTDOWN,
'on_extup': plexpy.CONFIG.TELEGRAM_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.TELEGRAM_ON_INTUP
+ 'on_intup': plexpy.CONFIG.TELEGRAM_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.TELEGRAM_ON_PMSUPDATE
},
{'name': 'Slack',
'id': AGENT_IDS['Slack'],
@@ -296,7 +309,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.SLACK_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.SLACK_ON_INTDOWN,
'on_extup': plexpy.CONFIG.SLACK_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.SLACK_ON_INTUP
+ 'on_intup': plexpy.CONFIG.SLACK_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.SLACK_ON_PMSUPDATE
},
{'name': 'Scripts',
'id': AGENT_IDS['Scripts'],
@@ -313,7 +327,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.SCRIPTS_ON_EXTDOWN,
'on_extup': plexpy.CONFIG.SCRIPTS_ON_EXTUP,
'on_intdown': plexpy.CONFIG.SCRIPTS_ON_INTDOWN,
- 'on_intup': plexpy.CONFIG.SCRIPTS_ON_INTUP
+ 'on_intup': plexpy.CONFIG.SCRIPTS_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.SCRIPTS_ON_PMSUPDATE
},
{'name': 'Facebook',
'id': AGENT_IDS['Facebook'],
@@ -330,7 +345,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.FACEBOOK_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.FACEBOOK_ON_INTDOWN,
'on_extup': plexpy.CONFIG.FACEBOOK_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.FACEBOOK_ON_INTUP
+ 'on_intup': plexpy.CONFIG.FACEBOOK_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.FACEBOOK_ON_PMSUPDATE
}
]
@@ -352,7 +368,8 @@ def available_notification_agents():
'on_extdown': plexpy.CONFIG.OSX_NOTIFY_ON_EXTDOWN,
'on_intdown': plexpy.CONFIG.OSX_NOTIFY_ON_INTDOWN,
'on_extup': plexpy.CONFIG.OSX_NOTIFY_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.OSX_NOTIFY_ON_INTUP
+ 'on_intup': plexpy.CONFIG.OSX_NOTIFY_ON_INTUP,
+ 'on_pmsupdate': plexpy.CONFIG.OSX_NOTIFY_ON_PMSUPDATE
})
return agents
@@ -1266,7 +1283,7 @@ class TwitterNotifier(object):
def return_config_options(self):
config_option = [{'label': 'Instructions',
- 'description': 'Step 1: Visit \
+ 'description': 'Step 1: Visit \
Twitter Apps to Create New App. A vaild "Website" is not required.
\
Step 2: Go to Keys and Access Tokens and click \
Create my access token.
\
@@ -1301,7 +1318,7 @@ class TwitterNotifier(object):
{'label': 'Include Subject Line',
'value': self.incl_subject,
'name': 'twitter_incl_subject',
- 'description': 'Include the subject line in the notifications.',
+ 'description': 'Include the subject line with the notifications.',
'input_type': 'checkbox'
}
]
@@ -1625,7 +1642,8 @@ class IFTTT(object):
config_option = [{'label': 'Ifttt Maker Channel Key',
'value': self.apikey,
'name': 'ifttt_key',
- 'description': 'Your Ifttt key. You can get a key from here.',
+ 'description': 'Your Ifttt key. You can get a key from'
+ ' here.',
'input_type': 'text'
},
{'label': 'Ifttt Event',
@@ -1699,19 +1717,23 @@ class TELEGRAM(object):
config_option = [{'label': 'Telegram Bot Token',
'value': self.bot_token,
'name': 'telegram_bot_token',
- 'description': 'Your Telegram bot token. Contact @BotFather on Telegram to get one.',
+ 'description': 'Your Telegram bot token. '
+ 'Contact @BotFather'
+ ' on Telegram to get one.',
'input_type': 'text'
},
{'label': 'Telegram Chat ID, Group ID, or Channel Username',
'value': self.chat_id,
'name': 'telegram_chat_id',
- 'description': 'Your Telegram Chat ID, Group ID, or @channelusername. Contact @myidbot on Telegram to get an ID.',
+ 'description': 'Your Telegram Chat ID, Group ID, or @channelusername. '
+ 'Contact @myidbot'
+ ' on Telegram to get an ID.',
'input_type': 'text'
},
{'label': 'Include Subject Line',
'value': self.incl_subject,
'name': 'telegram_incl_subject',
- 'description': 'Include the subject line in the notifications.',
+ 'description': 'Include the subject line with the notifications.',
'input_type': 'checkbox'
}
]
@@ -1809,7 +1831,7 @@ class SLACK(object):
{'label': 'Include Subject Line',
'value': self.incl_subject,
'name': 'slack_incl_subject',
- 'description': 'Include the subject line in the notifications.',
+ 'description': 'Include the subject line with the notifications.',
'input_type': 'checkbox'
}
]
@@ -1880,14 +1902,14 @@ class Scripts(object):
elif notify_action == 'resume':
script = plexpy.CONFIG.SCRIPTS_ON_RESUME_SCRIPT
+ elif notify_action == 'watched':
+ script = plexpy.CONFIG.SCRIPTS_ON_WATCHED_SCRIPT
+
elif notify_action == 'buffer':
script = plexpy.CONFIG.SCRIPTS_ON_BUFFER_SCRIPT
- elif notify_action == 'extdown':
- script = plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT
-
- elif notify_action == 'extup':
- script = plexpy.CONFIG.SCRIPTS_ON_EXTUP_SCRIPT
+ elif notify_action == 'created':
+ script = plexpy.CONFIG.SCRIPTS_ON_CREATED_SCRIPT
elif notify_action == 'intdown':
script = plexpy.CONFIG.SCRIPTS_ON_INTDOWN_SCRIPT
@@ -1895,11 +1917,14 @@ class Scripts(object):
elif notify_action == 'intup':
script = plexpy.CONFIG.SCRIPTS_ON_INTUP_SCRIPT
- elif notify_action == 'created':
- script = plexpy.CONFIG.SCRIPTS_ON_CREATED_SCRIPT
+ elif notify_action == 'extdown':
+ script = plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT
- elif notify_action == 'watched':
- script = plexpy.CONFIG.SCRIPTS_ON_WATCHED_SCRIPT
+ elif notify_action == 'extup':
+ script = plexpy.CONFIG.SCRIPTS_ON_EXTUP_SCRIPT
+
+ elif notify_action == 'pmsupdate':
+ script = plexpy.CONFIG.SCRIPTS_ON_PMSUPDATE_SCRIPT
else:
# For manual scripts
@@ -2041,13 +2066,6 @@ class Scripts(object):
'input_type': 'select',
'select_options': self.list_scripts()
},
- {'label': 'Plex Remote Access Down',
- 'value': plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT,
- 'name': 'scripts_on_extdown_script',
- 'description': 'Choose the script for Plex remote access down.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
{'label': 'Plex Server Down',
'value': plexpy.CONFIG.SCRIPTS_ON_INTDOWN_SCRIPT,
'name': 'scripts_on_intdown_script',
@@ -2055,6 +2073,20 @@ class Scripts(object):
'input_type': 'select',
'select_options': self.list_scripts()
},
+ {'label': 'Plex Server Back Up',
+ 'value': plexpy.CONFIG.SCRIPTS_ON_INTUP_SCRIPT,
+ 'name': 'scripts_on_intup_script',
+ 'description': 'Choose the script for Plex server back up.',
+ 'input_type': 'select',
+ 'select_options': self.list_scripts()
+ },
+ {'label': 'Plex Remote Access Down',
+ 'value': plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT,
+ 'name': 'scripts_on_extdown_script',
+ 'description': 'Choose the script for Plex remote access down.',
+ 'input_type': 'select',
+ 'select_options': self.list_scripts()
+ },
{'label': 'Plex Remote Access Back Up',
'value': plexpy.CONFIG.SCRIPTS_ON_EXTUP_SCRIPT,
'name': 'scripts_on_extup_script',
@@ -2062,10 +2094,10 @@ class Scripts(object):
'input_type': 'select',
'select_options': self.list_scripts()
},
- {'label': 'Plex Server Back Up',
- 'value': plexpy.CONFIG.SCRIPTS_ON_INTUP_SCRIPT,
- 'name': 'scripts_on_intup_script',
- 'description': 'Choose the script for Plex server back up.',
+ {'label': 'Plex Update Available',
+ 'value': plexpy.CONFIG.SCRIPTS_ON_PMSUPDATE_SCRIPT,
+ 'name': 'scripts_on_pmsupdate_script',
+ 'description': 'Choose the script for Plex update available.',
'input_type': 'select',
'select_options': self.list_scripts()
}
@@ -2082,6 +2114,7 @@ class FacebookNotifier(object):
self.app_id = plexpy.CONFIG.FACEBOOK_APP_ID
self.app_secret = plexpy.CONFIG.FACEBOOK_APP_SECRET
self.group_id = plexpy.CONFIG.FACEBOOK_GROUP
+ self.incl_pmslink = plexpy.CONFIG.FACEBOOK_INCL_PMSLINK
self.incl_poster = plexpy.CONFIG.FACEBOOK_INCL_POSTER
self.incl_subject = plexpy.CONFIG.FACEBOOK_INCL_SUBJECT
@@ -2139,10 +2172,27 @@ class FacebookNotifier(object):
poster_url = metadata.get('poster_url','')
if poster_url:
- if metadata['media_type'] == 'movie' or metadata['media_type'] == 'show':
+ if metadata['media_type'] == 'movie':
title = metadata['title']
subtitle = metadata['year']
rating_key = metadata['rating_key']
+ if metadata.get('imdb_url',''):
+ poster_link = metadata.get('imdb_url', '')
+ caption = 'View on IMDB.'
+ elif metadata.get('themoviedb_url',''):
+ poster_link = metadata.get('themoviedb_url', '')
+ caption = 'View on The Movie Database.'
+
+ elif metadata['media_type'] == 'show':
+ title = metadata['title']
+ subtitle = metadata['year']
+ rating_key = metadata['rating_key']
+ if metadata.get('thetvdb_url',''):
+ poster_link = metadata.get('thetvdb_url', '')
+ caption = 'View on TheTVDB.'
+ elif metadata.get('themoviedb_url',''):
+ poster_link = metadata.get('themoviedb_url', '')
+ caption = 'View on The Movie Database.'
elif metadata['media_type'] == 'episode':
title = '%s - %s' % (metadata['grandparent_title'], metadata['title'])
@@ -2150,26 +2200,44 @@ class FacebookNotifier(object):
'\xc2\xb7'.decode('utf8'),
metadata['media_index'])
rating_key = metadata['rating_key']
+ if metadata.get('thetvdb_url',''):
+ poster_link = metadata.get('thetvdb_url', '')
+ caption = 'View on TheTVDB.'
+ elif metadata.get('themoviedb_url',''):
+ poster_link = metadata.get('themoviedb_url', '')
+ caption = 'View on The Movie Database.'
elif metadata['media_type'] == 'artist':
title = metadata['title']
subtitle = ''
rating_key = metadata['rating_key']
+ if metadata.get('lastfm_url',''):
+ poster_link = metadata.get('lastfm_url', '')
+ caption = 'View on Last.fm.'
elif metadata['media_type'] == 'track':
title = '%s - %s' % (metadata['grandparent_title'], metadata['title'])
subtitle = metadata['parent_title']
rating_key = metadata['parent_rating_key']
-
- caption = 'View in Plex Web.'
+ if metadata.get('lastfm_url',''):
+ poster_link = metadata.get('lastfm_url', '')
+ caption = 'View on Last.fm.'
# Build Facebook post attachment
- attachment['link'] = 'http://app.plex.tv/web/app#!/server/' + plexpy.CONFIG.PMS_IDENTIFIER + \
- '/details/%2Flibrary%2Fmetadata%2F' + rating_key
+ if self.incl_pmslink:
+ caption = 'View on Plex Web.'
+ attachment['link'] = 'http://app.plex.tv/web/app#!/server/' + plexpy.CONFIG.PMS_IDENTIFIER + \
+ '/details/%2Flibrary%2Fmetadata%2F' + rating_key
+ attachment['caption'] = caption
+ elif poster_link:
+ attachment['link'] = poster_link
+ attachment['caption'] = caption
+ else:
+ attachment['link'] = poster_url
+
attachment['picture'] = poster_url
attachment['name'] = title
attachment['description'] = subtitle
- attachment['caption'] = caption
try:
api.put_wall_post(profile_id=self.group_id, message=message, attachment=attachment)
@@ -2186,7 +2254,7 @@ class FacebookNotifier(object):
def return_config_options(self):
config_option = [{'label': 'Instructions',
'description': 'Facebook notifications are currently experimental!
\
- Step 1: Visit \
+ Step 1: Visit \
Facebook Developers to add a new app using basic setup.
\
Step 2: Go to Settings > Basic and fill in a \
Contact Email.
\
@@ -2195,7 +2263,7 @@ class FacebookNotifier(object):
Step 4: Go to App Review and toggle public to Yes.
\
Step 5: Fill in the PlexPy URL below with the exact same URL from Step 3.
\
Step 6: Fill in the App ID and App Secret below.
\
- Step 7: Click the Request Authorization button below.
\
+ Step 7: Click the Request Authorization button below.
\
Step 8: Fill in the Group ID below.',
'input_type': 'help'
},
@@ -2232,13 +2300,20 @@ class FacebookNotifier(object):
{'label': 'Include Poster Image',
'value': self.incl_poster,
'name': 'facebook_incl_poster',
- 'description': 'Include a poster and link in the notifications.',
+ 'description': 'Include a poster with the notifications.',
+ 'input_type': 'checkbox'
+ },
+ {'label': 'Include Link to Plex Web',
+ 'value': self.incl_pmslink,
+ 'name': 'facebook_incl_pmslink',
+ 'description': 'Include a link to the media in Plex Web with the notifications.
'
+ 'If disabled, the link will go to IMDB, TVDB, TMDb, or Last.fm instead, if available.',
'input_type': 'checkbox'
},
{'label': 'Include Subject Line',
'value': self.incl_subject,
'name': 'facebook_incl_subject',
- 'description': 'Include the subject line in the notifications.',
+ 'description': 'Include the subject line with the notifications.',
'input_type': 'checkbox'
}
]
diff --git a/plexpy/plexwatch_import.py b/plexpy/plexwatch_import.py
index 533b5bdc..e9d6b13e 100644
--- a/plexpy/plexwatch_import.py
+++ b/plexpy/plexwatch_import.py
@@ -251,7 +251,7 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
hours=0, minutes=0, seconds=0)
plexpy.schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
hours=0, minutes=0, seconds=0)
- plexpy.schedule_job(activity_pinger.check_server_response, 'Check for server response',
+ plexpy.schedule_job(activity_pinger.check_server_response, 'Check for Plex remote access',
hours=0, minutes=0, seconds=0)
ap = activity_processor.ActivityProcessor()
@@ -302,7 +302,7 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
# Skip line if we don't have a ratingKey to work with
if not row['rating_key']:
- logger.error(u"PlexPy Importer :: Skipping record due to null ratingRey.")
+ logger.error(u"PlexPy Importer :: Skipping record due to null ratingKey.")
continue
# If the user_id no longer exists in the friends list, pull it from the xml.
diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py
index 05916f88..0aa76bbc 100644
--- a/plexpy/pmsconnect.py
+++ b/plexpy/pmsconnect.py
@@ -389,6 +389,38 @@ class PmsConnect(object):
return request
+ def put_updater(self, output_format=''):
+ """
+ Refresh updater status.
+
+ Optional parameters: output_format { dict, json }
+
+ Output: array
+ """
+ uri = '/updater/check?download=0'
+ request = self.request_handler.make_request(uri=uri,
+ proto=self.protocol,
+ request_type='PUT',
+ output_format=output_format)
+
+ return request
+
+ def get_updater(self, output_format=''):
+ """
+ Return updater status.
+
+ Optional parameters: output_format { dict, json }
+
+ Output: array
+ """
+ uri = '/updater/status'
+ request = self.request_handler.make_request(uri=uri,
+ proto=self.protocol,
+ request_type='GET',
+ output_format=output_format)
+
+ return request
+
def get_recently_added_details(self, section_id='', count='0'):
"""
Return processed and validated list of recently added items.
@@ -1008,7 +1040,7 @@ class PmsConnect(object):
'user_id': user_details['user_id'],
'friendly_name': user_details['friendly_name'],
'user_thumb': user_details['user_thumb'],
- 'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split(':')[-1],
+ 'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split('::ffff:')[-1],
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
'machine_id': machine_id,
@@ -1130,7 +1162,7 @@ class PmsConnect(object):
'user_id': user_details['user_id'],
'friendly_name': user_details['friendly_name'],
'user_thumb': user_details['user_thumb'],
- 'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split(':')[-1],
+ 'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split('::ffff:')[-1],
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
'machine_id': machine_id,
@@ -1188,7 +1220,7 @@ class PmsConnect(object):
'user_id': user_details['user_id'],
'friendly_name': user_details['friendly_name'],
'user_thumb': user_details['user_thumb'],
- 'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split(':')[-1],
+ 'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split('::ffff:')[-1],
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
'machine_id': machine_id,
@@ -1246,7 +1278,7 @@ class PmsConnect(object):
'user_id': user_details['user_id'],
'friendly_name': user_details['friendly_name'],
'user_thumb': user_details['user_thumb'],
- 'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split(':')[-1],
+ 'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split('::ffff:')[-1],
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
'machine_id': machine_id,
@@ -1337,7 +1369,7 @@ class PmsConnect(object):
'user_id': user_details['user_id'],
'friendly_name': user_details['friendly_name'],
'user_thumb': user_details['user_thumb'],
- 'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split(':')[-1],
+ 'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split('::ffff:')[-1],
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
'machine_id': machine_id,
@@ -1479,7 +1511,7 @@ class PmsConnect(object):
xml_head = identity.getElementsByTagName('MediaContainer')
except Exception as e:
logger.warn(u"PlexPy Pmsconnect :: Unable to parse XML for get_local_server_identity: %s." % e)
- return []
+ return {}
server_identity = {}
for a in xml_head:
@@ -1995,3 +2027,40 @@ class PmsConnect(object):
}
return server_response
+
+ def get_update_staus(self):
+ # Refresh the Plex updater status first
+ self.put_updater()
+ updater_status = self.get_updater(output_format='xml')
+
+ try:
+ xml_head = updater_status.getElementsByTagName('MediaContainer')
+ except Exception as e:
+ logger.warn(u"PlexPy Pmsconnect :: Unable to parse XML for get_update_staus: %s." % e)
+
+ # Catch the malformed XML on certain PMX version.
+ # XML parser helper returns empty list if there is an error parsing XML
+ if updater_status == []:
+ logger.warn(u"Plex API updater XML is broken on the current PMS version. Please update your PMS manually.")
+ logger.info(u"PlexPy is unable to check for Plex updates. Disabling check for Plex updates.")
+
+ # Disable check for Plex updates
+ plexpy.CONFIG.MONITOR_PMS_UPDATES = 0
+ plexpy.initialize_scheduler()
+ plexpy.CONFIG.write()
+
+ return {}
+
+ updater_info = {}
+ for a in xml_head:
+ if a.getElementsByTagName('Release'):
+ release = a.getElementsByTagName('Release')
+ for item in release:
+ updater_info = {'can_install': helpers.get_xml_attr(a, 'canInstall'),
+ 'download_url': helpers.get_xml_attr(a, 'downloadURL'),
+ 'version': helpers.get_xml_attr(item, 'version'),
+ 'state': helpers.get_xml_attr(item, 'state'),
+ 'changelog': helpers.get_xml_attr(item, 'fixed')
+ }
+
+ return updater_info
diff --git a/plexpy/users.py b/plexpy/users.py
index e4e7ee3c..9207ad0d 100644
--- a/plexpy/users.py
+++ b/plexpy/users.py
@@ -50,8 +50,7 @@ class Users(object):
'session_history_metadata.year',
'session_history_metadata.media_index',
'session_history_metadata.parent_media_index',
- 'session_history_media_info.video_decision',
- 'session_history_media_info.audio_decision',
+ 'session_history_media_info.transcode_decision',
'users.do_notify as do_notify',
'users.keep_history as keep_history'
]
@@ -117,8 +116,7 @@ class Users(object):
'year': item['year'],
'media_index': item['media_index'],
'parent_media_index': item['parent_media_index'],
- 'video_decision': item['video_decision'],
- 'audio_decision': item['audio_decision'],
+ 'transcode_decision': item['transcode_decision'],
'do_notify': helpers.checked(item['do_notify']),
'keep_history': helpers.checked(item['keep_history'])
}
@@ -154,8 +152,7 @@ class Users(object):
'session_history_metadata.year',
'session_history_metadata.media_index',
'session_history_metadata.parent_media_index',
- 'session_history_media_info.video_decision',
- 'session_history_media_info.audio_decision',
+ 'session_history_media_info.transcode_decision',
'session_history.user',
'session_history.user_id as custom_user_id',
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE \
@@ -213,8 +210,7 @@ class Users(object):
'year': item['year'],
'media_index': item['media_index'],
'parent_media_index': item['parent_media_index'],
- 'video_decision': item['video_decision'],
- 'audio_decision': item['audio_decision'],
+ 'transcode_decision': item['transcode_decision'],
'friendly_name': item['friendly_name']
}
@@ -245,7 +241,7 @@ class Users(object):
def get_details(self, user_id=None, user=None):
from plexpy import plextv
- default_return = {'user_id': None,
+ default_return = {'user_id': 0,
'username': 'Local',
'friendly_name': 'Local',
'user_thumb': common.DEFAULT_USER_THUMB,
@@ -254,7 +250,7 @@ class Users(object):
'is_allow_sync': 0,
'is_restricted': 0,
'do_notify': 0,
- 'keep_history': 0
+ 'keep_history': 1
}
if not user_id and not user:
@@ -316,7 +312,8 @@ class Users(object):
return user_details
else:
- logger.warn(u"PlexPy Users :: Unable to retrieve user from local database. Requesting user list refresh.")
+ logger.warn(u"PlexPy Users :: Unable to retrieve user %s from database. Requesting user list refresh."
+ % user_id if user_id else user)
# Let's first refresh the user list to make sure the user isn't newly added and not in the db yet
plextv.refresh_users()
@@ -326,7 +323,8 @@ class Users(object):
return user_details
else:
- logger.warn(u"PlexPy Users :: Unable to retrieve user from local database. Returning 'Local' user.")
+ logger.warn(u"PlexPy Users :: Unable to retrieve user %s from database. Returning 'Local' user."
+ % user_id if user_id else user)
# If there is no user data we must return something
# Use "Local" user to retain compatibility with PlexWatch database value
return default_return
diff --git a/plexpy/version.py b/plexpy/version.py
index fa9d3c56..f09e45cf 100644
--- a/plexpy/version.py
+++ b/plexpy/version.py
@@ -1,2 +1,2 @@
PLEXPY_VERSION = "master"
-PLEXPY_RELEASE_VERSION = "1.3.9"
+PLEXPY_RELEASE_VERSION = "1.3.10"
diff --git a/plexpy/web_socket.py b/plexpy/web_socket.py
index 2de97dcf..76fdfe3d 100644
--- a/plexpy/web_socket.py
+++ b/plexpy/web_socket.py
@@ -25,6 +25,7 @@ import websocket
name = 'websocket'
opcode_data = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY)
+ws_reconnect = False
def start_thread():
@@ -34,6 +35,11 @@ def start_thread():
threading.Thread(target=run).start()
+def reconnect():
+ global ws_reconnect
+ ws_reconnect = True
+
+
def run():
from websocket import create_connection
@@ -51,19 +57,21 @@ def run():
if plexpy.CONFIG.PMS_TOKEN:
uri += '?X-Plex-Token=' + plexpy.CONFIG.PMS_TOKEN
+ global ws_reconnect
+ ws_reconnect = False
ws_connected = False
reconnects = 0
# Try an open the websocket connection - if it fails after 15 retries fallback to polling
while not ws_connected and reconnects <= 15:
try:
- logger.info(u'PlexPy WebSocket :: Opening%s websocket, connection attempt %s.' % (secure, str(reconnects + 1)))
+ logger.info(u"PlexPy WebSocket :: Opening%s websocket, connection attempt %s." % (secure, str(reconnects + 1)))
ws = create_connection(uri)
reconnects = 0
ws_connected = True
- logger.info(u'PlexPy WebSocket :: Ready')
+ logger.info(u"PlexPy WebSocket :: Ready")
except IOError, e:
- logger.error(u'PlexPy WebSocket :: %s.' % e)
+ logger.error(u"PlexPy WebSocket :: %s." % e)
reconnects += 1
time.sleep(5)
@@ -81,22 +89,30 @@ def run():
if reconnects > 1:
time.sleep(5)
- logger.warn(u'PlexPy WebSocket :: Connection has closed, reconnecting...')
+ logger.warn(u"PlexPy WebSocket :: Connection has closed, reconnecting...")
try:
ws = create_connection(uri)
except IOError, e:
- logger.info(u'PlexPy WebSocket :: %s.' % e)
+ logger.info(u"PlexPy WebSocket :: %s." % e)
else:
+ ws.shutdown()
ws_connected = False
break
- if not ws_connected:
- logger.error(u'PlexPy WebSocket :: Connection unavailable, falling back to polling.')
+ # Check if we recieved a restart notification and close websocket connection cleanly
+ if ws_reconnect:
+ logger.info(u"PlexPy WebSocket :: Reconnecting websocket...")
+ ws.shutdown()
+ ws_connected = False
+ start_thread()
+
+ if not ws_connected and not ws_reconnect:
+ logger.error(u"PlexPy WebSocket :: Connection unavailable, falling back to polling.")
plexpy.POLLING_FAILOVER = True
plexpy.initialize_scheduler()
- logger.debug(u'PlexPy WebSocket :: Leaving thread.')
+ logger.debug(u"PlexPy WebSocket :: Leaving thread.")
def receive(ws):
@@ -124,7 +140,7 @@ def process(opcode, data):
try:
info = json.loads(data)
except Exception as ex:
- logger.warn(u'PlexPy WebSocket :: Error decoding message from websocket: %s' % ex)
+ logger.warn(u"PlexPy WebSocket :: Error decoding message from websocket: %s" % ex)
logger.debug(data)
return False
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index d50dcf5a..913f9e6a 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -13,7 +13,8 @@
# 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, libraries, database
+from plexpy import logger, notifiers, plextv, pmsconnect, common, log_reader, \
+ datafactory, graphs, users, libraries, database, web_socket
from plexpy.helpers import checked, addtoapi, get_ip, create_https_certificates
from mako.lookup import TemplateLookup
@@ -253,6 +254,18 @@ class WebInterface(object):
logger.warn(u"Unable to retrieve data for get_recently_added.")
return serve_template(templatename="recently_added.html", data=None)
+ @cherrypy.expose
+ def delete_temp_sessions(self):
+
+ result = database.delete_sessions()
+
+ if result:
+ cherrypy.response.headers['Content-type'] = 'application/json'
+ return json.dumps({'message': result})
+ else:
+ cherrypy.response.headers['Content-type'] = 'application/json'
+ return json.dumps({'message': 'no data received'})
+
##### Libraries #####
@@ -776,6 +789,10 @@ class WebInterface(object):
media_type = kwargs.get('media_type', "")
if media_type != 'all':
custom_where.append(['session_history.media_type', media_type])
+ if 'transcode_decision' in kwargs:
+ transcode_decision = kwargs.get('transcode_decision', "")
+ if transcode_decision:
+ custom_where.append(['session_history_media_info.transcode_decision', transcode_decision])
data_factory = datafactory.DataFactory()
history = data_factory.get_datatables_history(kwargs=kwargs, custom_where=custom_where, grouping=grouping, watched_percent=watched_percent)
@@ -989,9 +1006,9 @@ class WebInterface(object):
logger.warn(u"Unable to retrieve data for get_stream_type_by_top_10_platforms.")
@cherrypy.expose
- def history_table_modal(self, start_date=None, **kwargs):
+ def history_table_modal(self, **kwargs):
- return serve_template(templatename="history_table_modal.html", title="History Data", data=start_date)
+ return serve_template(templatename="history_table_modal.html", title="History Data", data=kwargs)
##### Sync #####
@@ -1091,6 +1108,19 @@ class WebInterface(object):
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(notifications)
+ @cherrypy.expose
+ @addtoapi()
+ def clearNotifyLogs(self, **kwargs):
+ data_factory = datafactory.DataFactory()
+ result = data_factory.delete_notification_log()
+
+ if result:
+ cherrypy.response.headers['Content-type'] = 'application/json'
+ return json.dumps({'message': result})
+ else:
+ cherrypy.response.headers['Content-type'] = 'application/json'
+ return json.dumps({'message': 'no data received'})
+
@cherrypy.expose
def clearLogs(self):
plexpy.LOG_LIST = []
@@ -1147,8 +1177,9 @@ class WebInterface(object):
"api_key": plexpy.CONFIG.API_KEY,
"update_db_interval": plexpy.CONFIG.UPDATE_DB_INTERVAL,
"freeze_db": checked(plexpy.CONFIG.FREEZE_DB),
- "log_dir": plexpy.CONFIG.LOG_DIR,
+ "backup_dir": plexpy.CONFIG.BACKUP_DIR,
"cache_dir": plexpy.CONFIG.CACHE_DIR,
+ "log_dir": plexpy.CONFIG.LOG_DIR,
"check_github": checked(plexpy.CONFIG.CHECK_GITHUB),
"interface_list": interface_list,
"cache_sizemb": plexpy.CONFIG.CACHE_SIZEMB,
@@ -1178,6 +1209,7 @@ class WebInterface(object):
"tv_notify_on_pause": checked(plexpy.CONFIG.TV_NOTIFY_ON_PAUSE),
"movie_notify_on_pause": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_PAUSE),
"music_notify_on_pause": checked(plexpy.CONFIG.MUSIC_NOTIFY_ON_PAUSE),
+ "monitor_pms_updates": checked(plexpy.CONFIG.MONITOR_PMS_UPDATES),
"monitor_remote_access": checked(plexpy.CONFIG.MONITOR_REMOTE_ACCESS),
"monitoring_interval": plexpy.CONFIG.MONITORING_INTERVAL,
"monitoring_use_websocket": checked(plexpy.CONFIG.MONITORING_USE_WEBSOCKET),
@@ -1219,6 +1251,8 @@ class WebInterface(object):
"notify_on_extup_body_text": plexpy.CONFIG.NOTIFY_ON_EXTUP_BODY_TEXT,
"notify_on_intup_subject_text": plexpy.CONFIG.NOTIFY_ON_INTUP_SUBJECT_TEXT,
"notify_on_intup_body_text": plexpy.CONFIG.NOTIFY_ON_INTUP_BODY_TEXT,
+ "notify_on_pmsupdate_subject_text": plexpy.CONFIG.NOTIFY_ON_PMSUPDATE_SUBJECT_TEXT,
+ "notify_on_pmsupdate_body_text": plexpy.CONFIG.NOTIFY_ON_PMSUPDATE_BODY_TEXT,
"notify_scripts_args_text": plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT,
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
"home_stats_type": checked(plexpy.CONFIG.HOME_STATS_TYPE),
@@ -1246,12 +1280,15 @@ class WebInterface(object):
"refresh_libraries_on_startup", "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_upload_posters",
- "notify_recently_added", "notify_recently_added_grandparent", "monitor_remote_access", "get_file_sizes"
+ "notify_recently_added", "notify_recently_added_grandparent",
+ "monitor_pms_updates", "monitor_remote_access", "get_file_sizes"
]
for checked_config in checked_configs:
if checked_config not in kwargs:
# checked items should be zero or one. if they were not sent then the item was not checked
- kwargs[checked_config] = '0'
+ kwargs[checked_config] = 0
+ else:
+ kwargs[checked_config] = 1
# If http password exists in config, do not overwrite when blank value received
if kwargs.get('http_password'):
@@ -1274,13 +1311,14 @@ class WebInterface(object):
if kwargs.get('monitoring_interval') != str(plexpy.CONFIG.MONITORING_INTERVAL) or \
kwargs.get('refresh_libraries_interval') != str(plexpy.CONFIG.REFRESH_LIBRARIES_INTERVAL) or \
kwargs.get('refresh_users_interval') != str(plexpy.CONFIG.REFRESH_USERS_INTERVAL) or \
- kwargs.get('notify_recently_added') != str(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED) or \
- kwargs.get('monitor_remote_access') != str(plexpy.CONFIG.MONITOR_REMOTE_ACCESS):
+ kwargs.get('notify_recently_added') != plexpy.CONFIG.NOTIFY_RECENTLY_ADDED or \
+ kwargs.get('monitor_pms_updates') != plexpy.CONFIG.MONITOR_PMS_UPDATES or \
+ kwargs.get('monitor_remote_access') != plexpy.CONFIG.MONITOR_REMOTE_ACCESS:
reschedule = True
# If we change the SSL setting for PMS or PMS remote setting, make sure we grab the new url.
- if kwargs.get('pms_ssl') != str(plexpy.CONFIG.PMS_SSL) or \
- kwargs.get('pms_is_remote') != str(plexpy.CONFIG.PMS_IS_REMOTE):
+ if kwargs.get('pms_ssl') != plexpy.CONFIG.PMS_SSL or \
+ kwargs.get('pms_is_remote') != plexpy.CONFIG.PMS_IS_REMOTE:
server_changed = True
# If we change the HTTPS setting, make sure we generate a new certificate.
@@ -1327,6 +1365,7 @@ class WebInterface(object):
if server_changed:
plextv.get_real_pms_url()
pmsconnect.get_server_friendly_name()
+ web_socket.reconnect()
# Reconfigure scheduler if intervals changed
if reschedule:
@@ -1630,11 +1669,16 @@ class WebInterface(object):
if source == 'history':
data_factory = datafactory.DataFactory()
metadata = data_factory.get_metadata_details(rating_key=rating_key)
+ poster_url = data_factory.get_poster_url(metadata=metadata)
+ metadata['poster_url'] = poster_url
else:
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_metadata_details(rating_key=rating_key, get_media_info=True)
if result:
metadata = result['metadata']
+ data_factory = datafactory.DataFactory()
+ poster_url = data_factory.get_poster_url(metadata=metadata)
+ metadata['poster_url'] = poster_url
if metadata:
return serve_template(templatename="info.html", data=metadata, title="Info", config=config, source=source)
@@ -1681,6 +1725,22 @@ class WebInterface(object):
return None
+ @cherrypy.expose
+ def delete_poster_url(self, poster_url=''):
+
+ if poster_url:
+ data_factory = datafactory.DataFactory()
+ result = data_factory.delete_poster_url(poster_url=poster_url)
+ else:
+ result = None
+
+ if result:
+ cherrypy.response.headers['Content-type'] = 'application/json'
+ return json.dumps({'message': result})
+ else:
+ cherrypy.response.headers['Content-type'] = 'application/json'
+ return json.dumps({'message': 'no data received'})
+
##### Search #####
@@ -2183,3 +2243,9 @@ class WebInterface(object):
a = Api()
a.checkParams(*args, **kwargs)
return a.fetchData()
+
+ @cherrypy.expose
+ def check_pms_updater(self):
+ pms_connect = pmsconnect.PmsConnect()
+ result = pms_connect.get_update_staus()
+ return json.dumps(result)
\ No newline at end of file