@@ -977,7 +1054,7 @@ $(document).ready(function() {
});
});
- // Load PlexWatch import modal
+ // Load notification agent config modal
$(".toggle-notification-config-modal").click(function() {
var configId = $(this).data('id');
$.ajax({
@@ -991,100 +1068,18 @@ $(document).ready(function() {
});
});
- $('.notify-toggle-icon').tooltip();
-
- $('.notify-toggle-icon').each(function() {
- if ($(this).data('config-value') == 1) {
- $(this).addClass("active");
- }
- });
-
- $('.notify-toggle-icon').click(function() {
- var configToggle = $(this).data('id');
- var toggle = $(this);
- if ($(this).hasClass("active")) {
- var data = {};
- data[$(this).data('config-name')] = 0;
- $.ajax({
- url: 'set_notification_config',
- data: data,
- async: true,
- success: function(data) {
- toggle.removeClass("active");
- }
- });
- } else {
- var data = {};
- data[$(this).data('config-name')] = 1;
- $.ajax({
- url: 'set_notification_config',
- data: data,
- async: true,
- success: function(data) {
- toggle.addClass("active");
- }
- });
- }
- });
-
- if ($("#tv_notify_enable").is(":checked"))
- {
- $("#tv_notify_options").show();
- }
- else
- {
- $("#tv_notify_options").hide();
- }
-
- $("#tv_notify_enable").click(function(){
- if ($("#tv_notify_enable").is(":checked"))
- {
- $("#tv_notify_options").slideDown();
- }
- else
- {
- $("#tv_notify_options").slideUp();
- }
- });
-
- if ($("#movie_notify_enable").is(":checked"))
- {
- $("#movie_notify_options").show();
- }
- else
- {
- $("#movie_notify_options").hide();
- }
-
- $("#movie_notify_enable").click(function(){
- if ($("#movie_notify_enable").is(":checked"))
- {
- $("#movie_notify_options").slideDown();
- }
- else
- {
- $("#movie_notify_options").slideUp();
- }
- });
-
- if ($("#music_notify_enable").is(":checked"))
- {
- $("#music_notify_options").show();
- }
- else
- {
- $("#music_notify_options").hide();
- }
-
- $("#music_notify_enable").click(function(){
- if ($("#music_notify_enable").is(":checked"))
- {
- $("#music_notify_options").slideDown();
- }
- else
- {
- $("#music_notify_options").slideUp();
+ // Load notification triggers config modal
+ $(".toggle-notification-triggers-modal").click(function() {
+ var configId = $(this).data('id');
+ $.ajax({
+ url: 'get_notification_agent_triggers',
+ data: { config_id: configId },
+ cache: false,
+ async: true,
+ complete: function(xhr, status) {
+ $("#notification-triggers-modal").html(xhr.responseText);
}
+ });
});
$('#osxnotifyregister').click(function () {
diff --git a/plexpy/__init__.py b/plexpy/__init__.py
index fe8285bd..4881c37d 100644
--- a/plexpy/__init__.py
+++ b/plexpy/__init__.py
@@ -348,7 +348,8 @@ def dbcheck():
'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, transcode_audio_channels INTEGER,'
- 'transcode_width INTEGER, transcode_height INTEGER)'
+ 'transcode_width INTEGER, transcode_height INTEGER, buffer_count INTEGER DEFAULT 0, '
+ 'buffer_last_triggered INTEGER)'
)
# session_history table :: This is a history table which logs essential stream details
@@ -529,7 +530,8 @@ def dbcheck():
c_db.execute(
'CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, '
'session_key INTEGER, rating_key INTEGER, user_id INTEGER, user TEXT, '
- 'agent_id INTEGER, agent_name TEXT, on_play INTEGER, on_stop INTEGER, on_watched INTEGER)'
+ 'agent_id INTEGER, agent_name TEXT, on_play INTEGER, on_stop INTEGER, on_watched INTEGER, '
+ 'on_pause INTEGER, on_resume INTEGER, on_buffer INTEGER)'
)
# Upgrade sessions table from earlier versions
@@ -550,6 +552,33 @@ def dbcheck():
'ALTER TABLE users ADD COLUMN keep_history INTEGER DEFAULT 1'
)
+ # Upgrade sessions table from earlier versions
+ try:
+ c_db.execute('SELECT on_pause from notify_log')
+ except sqlite3.OperationalError:
+ logger.debug(u"Altering database. Updating database table sessions.")
+ c_db.execute(
+ 'ALTER TABLE notify_log ADD COLUMN on_pause INTEGER'
+ )
+ c_db.execute(
+ 'ALTER TABLE notify_log ADD COLUMN on_resume INTEGER'
+ )
+ c_db.execute(
+ 'ALTER TABLE notify_log ADD COLUMN on_buffer INTEGER'
+ )
+
+ # Upgrade sessions table from earlier versions
+ try:
+ c_db.execute('SELECT buffer_count from sessions')
+ except sqlite3.OperationalError:
+ logger.debug(u"Altering database. Updating database table sessions.")
+ c_db.execute(
+ 'ALTER TABLE sessions ADD COLUMN buffer_count INTEGER DEFAULT 0'
+ )
+ c_db.execute(
+ 'ALTER TABLE sessions ADD COLUMN buffer_last_triggered INTEGER'
+ )
+
conn_db.commit()
c_db.close()
diff --git a/plexpy/config.py b/plexpy/config.py
index c67bfddb..55411795 100644
--- a/plexpy/config.py
+++ b/plexpy/config.py
@@ -39,7 +39,12 @@ _CONFIG_DEFINITIONS = {
'BOXCAR_TOKEN': (str, 'Boxcar', ''),
'BOXCAR_ON_PLAY': (int, 'Boxcar', 0),
'BOXCAR_ON_STOP': (int, 'Boxcar', 0),
+ 'BOXCAR_ON_PAUSE': (int, 'Boxcar', 0),
+ 'BOXCAR_ON_RESUME': (int, 'Boxcar', 0),
+ 'BOXCAR_ON_BUFFER': (int, 'Boxcar', 0),
'BOXCAR_ON_WATCHED': (int, 'Boxcar', 0),
+ 'BUFFER_THRESHOLD': (int, 'Monitoring', 3),
+ 'BUFFER_WAIT': (int, 'Monitoring', 900),
'CACHE_DIR': (str, 'General', ''),
'CACHE_SIZEMB': (int, 'Advanced', 32),
'CHECK_GITHUB': (int, 'General', 1),
@@ -58,6 +63,9 @@ _CONFIG_DEFINITIONS = {
'EMAIL_TLS': (int, 'Email', 0),
'EMAIL_ON_PLAY': (int, 'Email', 0),
'EMAIL_ON_STOP': (int, 'Email', 0),
+ 'EMAIL_ON_PAUSE': (int, 'Email', 0),
+ 'EMAIL_ON_RESUME': (int, 'Email', 0),
+ 'EMAIL_ON_BUFFER': (int, 'Email', 0),
'EMAIL_ON_WATCHED': (int, 'Email', 0),
'ENABLE_HTTPS': (int, 'General', 0),
'FIRST_RUN_COMPLETE': (int, 'General', 0),
@@ -70,6 +78,9 @@ _CONFIG_DEFINITIONS = {
'GROWL_PASSWORD': (str, 'Growl', ''),
'GROWL_ON_PLAY': (int, 'Growl', 0),
'GROWL_ON_STOP': (int, 'Growl', 0),
+ 'GROWL_ON_PAUSE': (int, 'Growl', 0),
+ 'GROWL_ON_RESUME': (int, 'Growl', 0),
+ 'GROWL_ON_BUFFER': (int, 'Growl', 0),
'GROWL_ON_WATCHED': (int, 'Growl', 0),
'HOME_STATS_LENGTH': (int, 'General', 30),
'HTTPS_CERT': (str, 'General', ''),
@@ -101,18 +112,30 @@ _CONFIG_DEFINITIONS = {
'NMA_PRIORITY': (int, 'NMA', 0),
'NMA_ON_PLAY': (int, 'NMA', 0),
'NMA_ON_STOP': (int, 'NMA', 0),
+ 'NMA_ON_PAUSE': (int, 'NMA', 0),
+ 'NMA_ON_RESUME': (int, 'NMA', 0),
+ 'NMA_ON_BUFFER': (int, 'NMA', 0),
'NMA_ON_WATCHED': (int, 'NMA', 0),
'NOTIFY_WATCHED_PERCENT': (int, 'Monitoring', 85),
'NOTIFY_ON_START_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
'NOTIFY_ON_START_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) started playing {title}.'),
'NOTIFY_ON_STOP_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
'NOTIFY_ON_STOP_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has stopped {title}.'),
+ 'NOTIFY_ON_PAUSE_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
+ 'NOTIFY_ON_PAUSE_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has paused {title}.'),
+ 'NOTIFY_ON_RESUME_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
+ 'NOTIFY_ON_RESUME_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has resumed {title}.'),
+ 'NOTIFY_ON_BUFFER_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
+ 'NOTIFY_ON_BUFFER_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) is buffering {title}.'),
'NOTIFY_ON_WATCHED_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
'NOTIFY_ON_WATCHED_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has watched {title}.'),
'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/PlexPy'),
'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_PLAY': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_STOP': (int, 'OSX_Notify', 0),
+ 'OSX_NOTIFY_ON_PAUSE': (int, 'OSX_Notify', 0),
+ 'OSX_NOTIFY_ON_RESUME': (int, 'OSX_Notify', 0),
+ 'OSX_NOTIFY_ON_BUFFER': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_WATCHED': (int, 'OSX_Notify', 0),
'PLEX_CLIENT_HOST': (str, 'Plex', ''),
'PLEX_ENABLED': (int, 'Plex', 0),
@@ -120,17 +143,26 @@ _CONFIG_DEFINITIONS = {
'PLEX_USERNAME': (str, 'Plex', ''),
'PLEX_ON_PLAY': (int, 'Plex', 0),
'PLEX_ON_STOP': (int, 'Plex', 0),
+ 'PLEX_ON_PAUSE': (int, 'Plex', 0),
+ 'PLEX_ON_RESUME': (int, 'Plex', 0),
+ 'PLEX_ON_BUFFER': (int, 'Plex', 0),
'PLEX_ON_WATCHED': (int, 'Plex', 0),
'PROWL_ENABLED': (int, 'Prowl', 0),
'PROWL_KEYS': (str, 'Prowl', ''),
'PROWL_PRIORITY': (int, 'Prowl', 0),
'PROWL_ON_PLAY': (int, 'Prowl', 0),
'PROWL_ON_STOP': (int, 'Prowl', 0),
+ 'PROWL_ON_PAUSE': (int, 'Prowl', 0),
+ 'PROWL_ON_RESUME': (int, 'Prowl', 0),
+ 'PROWL_ON_BUFFER': (int, 'Prowl', 0),
'PROWL_ON_WATCHED': (int, 'Prowl', 0),
'PUSHALOT_APIKEY': (str, 'Pushalot', ''),
'PUSHALOT_ENABLED': (int, 'Pushalot', 0),
'PUSHALOT_ON_PLAY': (int, 'Pushalot', 0),
'PUSHALOT_ON_STOP': (int, 'Pushalot', 0),
+ 'PUSHALOT_ON_PAUSE': (int, 'Pushalot', 0),
+ 'PUSHALOT_ON_RESUME': (int, 'Pushalot', 0),
+ 'PUSHALOT_ON_BUFFER': (int, 'Pushalot', 0),
'PUSHALOT_ON_WATCHED': (int, 'Pushalot', 0),
'PUSHBULLET_APIKEY': (str, 'PushBullet', ''),
'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''),
@@ -138,6 +170,9 @@ _CONFIG_DEFINITIONS = {
'PUSHBULLET_ENABLED': (int, 'PushBullet', 0),
'PUSHBULLET_ON_PLAY': (int, 'PushBullet', 0),
'PUSHBULLET_ON_STOP': (int, 'PushBullet', 0),
+ 'PUSHBULLET_ON_PAUSE': (int, 'PushBullet', 0),
+ 'PUSHBULLET_ON_RESUME': (int, 'PushBullet', 0),
+ 'PUSHBULLET_ON_BUFFER': (int, 'PushBullet', 0),
'PUSHBULLET_ON_WATCHED': (int, 'PushBullet', 0),
'PUSHOVER_APITOKEN': (str, 'Pushover', ''),
'PUSHOVER_ENABLED': (int, 'Pushover', 0),
@@ -145,6 +180,9 @@ _CONFIG_DEFINITIONS = {
'PUSHOVER_PRIORITY': (int, 'Pushover', 0),
'PUSHOVER_ON_PLAY': (int, 'Pushover', 0),
'PUSHOVER_ON_STOP': (int, 'Pushover', 0),
+ 'PUSHOVER_ON_PAUSE': (int, 'Pushover', 0),
+ 'PUSHOVER_ON_RESUME': (int, 'Pushover', 0),
+ 'PUSHOVER_ON_BUFFER': (int, 'Pushover', 0),
'PUSHOVER_ON_WATCHED': (int, 'Pushover', 0),
'REFRESH_USERS_INTERVAL': (int, 'Monitoring', 12),
'REFRESH_USERS_ON_STARTUP': (int, 'Monitoring', 1),
@@ -165,6 +203,9 @@ _CONFIG_DEFINITIONS = {
'XBMC_USERNAME': (str, 'XBMC', ''),
'XBMC_ON_PLAY': (int, 'XBMC', 0),
'XBMC_ON_STOP': (int, 'XBMC', 0),
+ 'XBMC_ON_PAUSE': (int, 'XBMC', 0),
+ 'XBMC_ON_RESUME': (int, 'XBMC', 0),
+ 'XBMC_ON_BUFFER': (int, 'XBMC', 0),
'XBMC_ON_WATCHED': (int, 'XBMC', 0)
}
# pylint:disable=R0902
diff --git a/plexpy/monitor.py b/plexpy/monitor.py
index 418b00e5..f3b13355 100644
--- a/plexpy/monitor.py
+++ b/plexpy/monitor.py
@@ -59,6 +59,11 @@ def check_active_sessions():
# 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()
if stream['state'] == 'paused':
# The stream is still paused so we need to increment the paused_counter
# Using the set config parameter as the interval, probably not the most accurate but
@@ -67,6 +72,47 @@ def check_active_sessions():
monitor_db.action('UPDATE sessions SET paused_counter = ? '
'WHERE session_key = ? AND rating_key = ?',
[paused_counter, stream['session_key'], stream['rating_key']])
+ if session['state'] == 'buffering':
+ # The stream is buffering so we need to increment the buffer_count
+ # We're going just increment on every monitor ping,
+ # would be difficult to keep track otherwise
+ monitor_db.action('UPDATE sessions SET buffer_count = buffer_count + 1 '
+ 'WHERE session_key = ? AND rating_key = ?',
+ [stream['session_key'], stream['rating_key']])
+
+ # Check the current buffer count and last buffer to determine if we should notify
+ buffer_values = monitor_db.select('SELECT buffer_count, buffer_last_triggered '
+ 'FROM sessions '
+ 'WHERE session_key = ? AND rating_key = ?',
+ [stream['session_key'], stream['rating_key']])
+
+ if buffer_values[0]['buffer_count'] >= plexpy.CONFIG.BUFFER_THRESHOLD:
+ # Push any notifications -
+ # Push it on it's own thread so we don't hold up our db actions
+ # Our first buffer notification
+ if buffer_values[0]['buffer_count'] == plexpy.CONFIG.BUFFER_THRESHOLD:
+ logger.info(u"PlexPy Monitor :: User '%s' has triggered a buffer warning."
+ % stream['user'])
+ # Set the buffer trigger time
+ monitor_db.action('UPDATE sessions '
+ 'SET buffer_last_triggered = strftime("%s","now") '
+ '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()
+ else:
+ # Subsequent buffer notifications after wait time
+ if int(time.time()) > buffer_values[0]['buffer_last_triggered'] + \
+ plexpy.CONFIG.BUFFER_WAIT:
+ logger.info(u"PlexPy Monitor :: User '%s' has triggered multiple buffer warnings."
+ % stream['user'])
+ 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][0], buffer_values[0][1]))
+
# Check if the user has reached the offset in the media we defined as the "watched" percent
# Don't trigger if state is buffer as some clients push the progress to the end when
# buffering on start.
diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py
index a7072b56..0a15026c 100644
--- a/plexpy/notification_handler.py
+++ b/plexpy/notification_handler.py
@@ -52,6 +52,33 @@ def notify(stream_data=None, notify_action=None):
set_notify_state(session=stream_data, state='stop', agent_info=agent)
+ elif agent['on_pause'] and notify_action == 'pause':
+ # Build and send notification
+ notify_strings = build_notify_text(session=stream_data, state=notify_action)
+ notifiers.send_notification(config_id=agent['id'],
+ subject=notify_strings[0],
+ body=notify_strings[1])
+
+ set_notify_state(session=stream_data, state='pause', agent_info=agent)
+
+ elif agent['on_resume'] and notify_action == 'resume':
+ # Build and send notification
+ notify_strings = build_notify_text(session=stream_data, state=notify_action)
+ notifiers.send_notification(config_id=agent['id'],
+ subject=notify_strings[0],
+ body=notify_strings[1])
+
+ set_notify_state(session=stream_data, state='resume', agent_info=agent)
+
+ elif agent['on_buffer'] and notify_action == 'buffer':
+ # Build and send notification
+ notify_strings = build_notify_text(session=stream_data, state=notify_action)
+ notifiers.send_notification(config_id=agent['id'],
+ subject=notify_strings[0],
+ body=notify_strings[1])
+
+ set_notify_state(session=stream_data, state='buffer', agent_info=agent)
+
elif agent['on_watched'] and notify_action == 'watched':
# Get the current states for notifications from our db
notify_states = get_notify_state(session=stream_data)
@@ -100,6 +127,33 @@ def notify(stream_data=None, notify_action=None):
# Set the notification state in the db
set_notify_state(session=stream_data, state='stop', agent_info=agent)
+ elif agent['on_pause'] and notify_action == 'pause':
+ # Build and send notification
+ notify_strings = build_notify_text(session=stream_data, state=notify_action)
+ notifiers.send_notification(config_id=agent['id'],
+ subject=notify_strings[0],
+ body=notify_strings[1])
+ # Set the notification state in the db
+ set_notify_state(session=stream_data, state='pause', agent_info=agent)
+
+ elif agent['on_resume'] and notify_action == 'resume':
+ # Build and send notification
+ notify_strings = build_notify_text(session=stream_data, state=notify_action)
+ notifiers.send_notification(config_id=agent['id'],
+ subject=notify_strings[0],
+ body=notify_strings[1])
+ # Set the notification state in the db
+ set_notify_state(session=stream_data, state='resume', agent_info=agent)
+
+ elif agent['on_buffer'] and notify_action == 'buffer':
+ # Build and send notification
+ notify_strings = build_notify_text(session=stream_data, state=notify_action)
+ notifiers.send_notification(config_id=agent['id'],
+ subject=notify_strings[0],
+ body=notify_strings[1])
+ # Set the notification state in the db
+ set_notify_state(session=stream_data, state='buffer', agent_info=agent)
+
elif stream_data['media_type'] == 'clip':
pass
else:
@@ -110,7 +164,7 @@ def notify(stream_data=None, notify_action=None):
def get_notify_state(session):
monitor_db = database.MonitorDatabase()
- result = monitor_db.select('SELECT on_play, on_stop, on_watched, agent_id '
+ result = monitor_db.select('SELECT on_play, on_stop, on_pause, on_resume, on_buffer, on_watched, agent_id '
'FROM notify_log '
'WHERE session_key = ? '
'AND rating_key = ? '
@@ -121,8 +175,11 @@ def get_notify_state(session):
for item in result:
notify_state = {'on_play': item[0],
'on_stop': item[1],
- 'on_watched': item[2],
- 'agent_id': item[3]}
+ 'on_pause': item[2],
+ 'on_resume': item[3],
+ 'on_buffer': item[4],
+ 'on_watched': item[5],
+ 'agent_id': item[6]}
notify_states.append(notify_state)
return notify_states
@@ -136,6 +193,12 @@ def set_notify_state(session, state, agent_info):
values = {'on_play': int(time.time())}
elif state == 'stop':
values = {'on_stop': int(time.time())}
+ elif state == 'pause':
+ values = {'on_pause': int(time.time())}
+ elif state == 'resume':
+ values = {'on_resume': int(time.time())}
+ elif state == 'buffer':
+ values = {'on_buffer': int(time.time())}
elif state == 'watched':
values = {'on_watched': int(time.time())}
else:
@@ -173,34 +236,28 @@ def build_notify_text(session, state):
if session['media_type'] == 'episode':
# Regex pattern to remove the text in the tags we don't want
pattern = re.compile('[^>]+.|[^>]+.', re.IGNORECASE)
-
- # 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_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))
elif session['media_type'] == 'movie':
# Regex pattern to remove the text in the tags we don't want
pattern = re.compile('[^>]+.|[^>]+.', re.IGNORECASE)
-
- # 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_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))
elif session['media_type'] == 'track':
# Regex pattern to remove the text in the tags we don't want
pattern = re.compile('[^>]+.|[^>]+.', re.IGNORECASE)
+ else:
+ pattern = None
+ if session['media_type'] == 'episode' or session['media_type'] == 'movie' or session['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))
else:
@@ -208,6 +265,12 @@ def build_notify_text(session, state):
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
@@ -310,6 +373,78 @@ def build_notify_text(session, state):
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
+ return [subject_text, body_text]
+ else:
+ return [subject_text, body_text]
+ elif state == 'pause':
+ # Default body text
+ body_text = '%s (%s) has paused %s' % (session['friendly_name'],
+ session['player'],
+ full_title)
+
+ if on_pause_subject and on_pause_body:
+ try:
+ subject_text = on_pause_subject.format(**available_params)
+ except LookupError, e:
+ logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
+ except:
+ logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
+
+ try:
+ body_text = on_pause_body.format(**available_params)
+ except LookupError, e:
+ logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
+ except:
+ logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
+
+ return [subject_text, body_text]
+ else:
+ return [subject_text, body_text]
+ elif state == 'resume':
+ # Default body text
+ body_text = '%s (%s) has resumed %s' % (session['friendly_name'],
+ session['player'],
+ full_title)
+
+ if on_resume_subject and on_resume_body:
+ try:
+ subject_text = on_resume_subject.format(**available_params)
+ except LookupError, e:
+ logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
+ except:
+ logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
+
+ try:
+ body_text = on_resume_body.format(**available_params)
+ except LookupError, e:
+ logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
+ except:
+ logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
+
+ return [subject_text, body_text]
+ else:
+ return [subject_text, body_text]
+ elif state == 'buffer':
+ # Default body text
+ body_text = '%s (%s) is buffering %s' % (session['friendly_name'],
+ session['player'],
+ full_title)
+
+ if on_buffer_subject and on_buffer_body:
+ try:
+ subject_text = on_buffer_subject.format(**available_params)
+ except LookupError, e:
+ logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
+ except:
+ logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
+
+ try:
+ body_text = on_buffer_body.format(**available_params)
+ except LookupError, e:
+ logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
+ except:
+ logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
+
return [subject_text, body_text]
else:
return [subject_text, body_text]
diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py
index 2e28f13a..2c348b5a 100644
--- a/plexpy/notifiers.py
+++ b/plexpy/notifiers.py
@@ -59,6 +59,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.GROWL_ENABLED),
'on_play': plexpy.CONFIG.GROWL_ON_PLAY,
'on_stop': plexpy.CONFIG.GROWL_ON_STOP,
+ 'on_pause': plexpy.CONFIG.GROWL_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.GROWL_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.GROWL_ON_BUFFER,
'on_watched': plexpy.CONFIG.GROWL_ON_WATCHED
},
{'name': 'Prowl',
@@ -68,6 +71,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.PROWL_ENABLED),
'on_play': plexpy.CONFIG.PROWL_ON_PLAY,
'on_stop': plexpy.CONFIG.PROWL_ON_STOP,
+ 'on_pause': plexpy.CONFIG.PROWL_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.PROWL_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.PROWL_ON_BUFFER,
'on_watched': plexpy.CONFIG.PROWL_ON_WATCHED
},
{'name': 'XBMC',
@@ -77,6 +83,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.XBMC_ENABLED),
'on_play': plexpy.CONFIG.XBMC_ON_PLAY,
'on_stop': plexpy.CONFIG.XBMC_ON_STOP,
+ 'on_pause': plexpy.CONFIG.XBMC_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.XBMC_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.XBMC_ON_BUFFER,
'on_watched': plexpy.CONFIG.XBMC_ON_WATCHED
},
{'name': 'Plex',
@@ -86,6 +95,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.PLEX_ENABLED),
'on_play': plexpy.CONFIG.PLEX_ON_PLAY,
'on_stop': plexpy.CONFIG.PLEX_ON_STOP,
+ 'on_pause': plexpy.CONFIG.PLEX_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.PLEX_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.PLEX_ON_BUFFER,
'on_watched': plexpy.CONFIG.PLEX_ON_WATCHED
},
{'name': 'NotifyMyAndroid',
@@ -95,6 +107,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.NMA_ENABLED),
'on_play': plexpy.CONFIG.NMA_ON_PLAY,
'on_stop': plexpy.CONFIG.NMA_ON_STOP,
+ 'on_pause': plexpy.CONFIG.NMA_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.NMA_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.NMA_ON_BUFFER,
'on_watched': plexpy.CONFIG.NMA_ON_WATCHED
},
{'name': 'Pushalot',
@@ -104,6 +119,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.PUSHALOT_ENABLED),
'on_play': plexpy.CONFIG.PUSHALOT_ON_PLAY,
'on_stop': plexpy.CONFIG.PUSHALOT_ON_STOP,
+ 'on_pause': plexpy.CONFIG.PUSHALOT_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.PUSHALOT_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.PUSHALOT_ON_BUFFER,
'on_watched': plexpy.CONFIG.PUSHALOT_ON_WATCHED
},
{'name': 'Pushbullet',
@@ -113,6 +131,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.PUSHBULLET_ENABLED),
'on_play': plexpy.CONFIG.PUSHBULLET_ON_PLAY,
'on_stop': plexpy.CONFIG.PUSHBULLET_ON_STOP,
+ 'on_pause': plexpy.CONFIG.PUSHBULLET_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.PUSHBULLET_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.PUSHBULLET_ON_BUFFER,
'on_watched': plexpy.CONFIG.PUSHBULLET_ON_WATCHED
},
{'name': 'Pushover',
@@ -122,6 +143,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.PUSHOVER_ENABLED),
'on_play': plexpy.CONFIG.PUSHOVER_ON_PLAY,
'on_stop': plexpy.CONFIG.PUSHOVER_ON_STOP,
+ 'on_pause': plexpy.CONFIG.PUSHOVER_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.PUSHOVER_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.PUSHOVER_ON_BUFFER,
'on_watched': plexpy.CONFIG.PUSHOVER_ON_WATCHED
},
{'name': 'Boxcar2',
@@ -131,6 +155,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.BOXCAR_ENABLED),
'on_play': plexpy.CONFIG.BOXCAR_ON_PLAY,
'on_stop': plexpy.CONFIG.BOXCAR_ON_STOP,
+ 'on_pause': plexpy.CONFIG.BOXCAR_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.BOXCAR_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.BOXCAR_ON_BUFFER,
'on_watched': plexpy.CONFIG.BOXCAR_ON_WATCHED
},
{'name': 'E-mail',
@@ -140,6 +167,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.EMAIL_ENABLED),
'on_play': plexpy.CONFIG.EMAIL_ON_PLAY,
'on_stop': plexpy.CONFIG.EMAIL_ON_STOP,
+ 'on_pause': plexpy.CONFIG.EMAIL_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.EMAIL_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.EMAIL_ON_BUFFER,
'on_watched': plexpy.CONFIG.EMAIL_ON_WATCHED
}
]
@@ -154,6 +184,9 @@ def available_notification_agents():
'state': checked(plexpy.CONFIG.OSX_NOTIFY_ENABLED),
'on_play': plexpy.CONFIG.OSX_NOTIFY_ON_PLAY,
'on_stop': plexpy.CONFIG.OSX_NOTIFY_ON_STOP,
+ 'on_pause': plexpy.CONFIG.OSX_NOTIFY_ON_PAUSE,
+ 'on_resume': plexpy.CONFIG.OSX_NOTIFY_ON_RESUME,
+ 'on_buffer': plexpy.CONFIG.OSX_NOTIFY_ON_BUFFER,
'on_watched': plexpy.CONFIG.OSX_NOTIFY_ON_WATCHED
})
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index 9c1fe603..90013df9 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -436,9 +436,17 @@ class WebInterface(object):
"notify_on_start_body_text": plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT,
"notify_on_stop_subject_text": plexpy.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT,
"notify_on_stop_body_text": plexpy.CONFIG.NOTIFY_ON_STOP_BODY_TEXT,
+ "notify_on_pause_subject_text": plexpy.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT,
+ "notify_on_pause_body_text": plexpy.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT,
+ "notify_on_resume_subject_text": plexpy.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT,
+ "notify_on_resume_body_text": plexpy.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT,
+ "notify_on_buffer_subject_text": plexpy.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT,
+ "notify_on_buffer_body_text": plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT,
"notify_on_watched_subject_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT,
"notify_on_watched_body_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT,
- "home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH
+ "home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
+ "buffer_threshold": plexpy.CONFIG.BUFFER_THRESHOLD,
+ "buffer_wait": plexpy.CONFIG.BUFFER_WAIT
}
return serve_template(templatename="settings.html", title="Settings", config=config)
@@ -1226,3 +1234,19 @@ class WebInterface(object):
return serve_template(templatename="notification_config.html", title="Notification Configuration",
data=config, checkboxes=checkboxes)
+
+ @cherrypy.expose
+ def get_notification_agent_triggers(self, config_id, **kwargs):
+ if config_id.isdigit():
+ agents = notifiers.available_notification_agents()
+ for agent in agents:
+ if int(config_id) == agent['id']:
+ this_agent = agent
+ break
+ else:
+ this_agent = None
+ else:
+ return None
+
+ return serve_template(templatename="notification_triggers_modal.html", title="Notification Triggers",
+ data=this_agent)