Stage three of notification rewrite. Page cache refresh required.

Watched notifications added with configurable cutoff percent.
Styling fixes.
Add information back to settings page for required restart on web interface changes.
Use user_id on user link in activity pane.
Added channels option to Pushbullet.
Possible fix for text encoding issues in Plexwatch importer.
Thread the notifications.
Move most of the notification logic to the notification handler.
This commit is contained in:
Tim 2015-07-24 00:53:31 +02:00
parent 8a16fcfbb6
commit b394ebc1a3
10 changed files with 194 additions and 65 deletions

View file

@ -8416,24 +8416,23 @@ ol.test >li {
background-color: #2f2f2f; background-color: #2f2f2f;
} }
.stacked-configs > li > span > a { .stacked-configs > li > span > a.toggle-right {
float: right; float: right;
color: #999; color: #999;
padding-left: 10px; padding-left: 10px;
} }
.stacked-configs > li > span > a.toggle-left {
color: #444;
padding-right: 2px;
}
.stacked-configs > li > span > a:hover { .stacked-configs > li > span > a:hover {
color: #eee; color: #eee;
} }
.stacked-configs > li > span > i { .stacked-configs > li > span > a.active {
cursor: pointer; color: #eb8600;
padding-right: 2px;
color: #444;
}
.stacked-configs > li > span > i:hover {
color: #eee;
} }
.stacked-configs > li > span > input[type='checkbox'] { .stacked-configs > li > span > input[type='checkbox'] {

View file

@ -99,7 +99,11 @@ DOCUMENTATION :: END
<div class="dashboard-activity-metadata-platform" id="platform-${a['session_key']}"> <div class="dashboard-activity-metadata-platform" id="platform-${a['session_key']}">
</div> </div>
<div class="dashboard-activity-metadata-user"> <div class="dashboard-activity-metadata-user">
% if a['user_id']:
<a href="user?user_id=${a['user_id']}">${a['friendly_name']}</a> is ${a['state']}
% else:
<a href="user?user=${a['user']}">${a['friendly_name']}</a> is ${a['state']} <a href="user?user=${a['user']}">${a['friendly_name']}</a> is ${a['state']}
% endif
</div> </div>
<div class="dashboard-activity-metadata-title"> <div class="dashboard-activity-metadata-title">
% if a['type'] == 'episode': % if a['type'] == 'episode':

View file

@ -80,6 +80,7 @@
<div class="wellheader"> <div class="wellheader">
<h3>Web Interface</h3> <h3>Web Interface</h3>
</div> </div>
<p class="help-block">Web interface changes require a restart.</p>
<div class="form-group"> <div class="form-group">
<label for="http_host">HTTP Host</label> <label for="http_host">HTTP Host</label>
<input type="text" id="http_host" name="http_host" value="${config['http_host']}" size="30" data-parsley-trigger="change" required> <input type="text" id="http_host" name="http_host" value="${config['http_host']}" size="30" data-parsley-trigger="change" required>
@ -116,6 +117,7 @@
<div class="wellheader"> <div class="wellheader">
<h3>Authentication</h3> <h3>Authentication</h3>
</div> </div>
<p class="help-block">Authentication changes require a restart.</p>
<div class="form-group"> <div class="form-group">
<label for="http_username">HTTP Username</label> <label for="http_username">HTTP Username</label>
<input type="text" id="http_username" name="http_username" value="${config['http_username']}" size="30"> <input type="text" id="http_username" name="http_username" value="${config['http_username']}" size="30">
@ -273,7 +275,7 @@
<div role="tabpanel" class="tab-pane" id="tabs-8"> <div role="tabpanel" class="tab-pane" id="tabs-8">
<fieldset> <fieldset>
<div class="wellheader"> <div class="wellheader">
<h3>Global Notifications</h3> <h3>Global Notification Toggles</h3>
</div> </div>
<div class="checkbox"> <div class="checkbox">
<input type="checkbox" name="movie_notify_enable" id="movie_notify_enable" value="1" ${config['movie_notify_enable']}> Enable Movie and TV Notifications <input type="checkbox" name="movie_notify_enable" id="movie_notify_enable" value="1" ${config['movie_notify_enable']}> Enable Movie and TV Notifications
@ -282,6 +284,16 @@
<input type="checkbox" name="music_notify_enable" id="music_notify_enable" value="1" ${config['music_notify_enable']}> Enable Music Notifications <input type="checkbox" name="music_notify_enable" id="music_notify_enable" value="1" ${config['music_notify_enable']}> Enable Music Notifications
</div> </div>
</fieldset> </fieldset>
<div class="wellheader">
<h3>Notification Tuning</h3>
</div>
<fieldset>
<div class="form-group">
<label for="notify_watched_percent">Watched Percent</label>
<input type="text" data-parsley-type="integer" id="notify_watched_percent" name="notify_watched_percent" value="${config['notify_watched_percent']}" size="5" data-parsley-range="[50,95]" data-parsley-trigger="change" required>
<p class="help-block">Set the progress percentage of when a watched notification should be triggered. Minimum 50, Maximum 95.</p>
</div>
</fieldset>
<input type="button" class="rounded rounded-primary save-button" value="Save" data-success="Changes saved successfully"> <input type="button" class="rounded rounded-primary save-button" value="Save" data-success="Changes saved successfully">
</div> </div>
<div role="tabpanel" class="tab-pane" id="tabs-9"> <div role="tabpanel" class="tab-pane" id="tabs-9">
@ -299,12 +311,12 @@
<li> <li>
<span> <span>
<!--<input type="checkbox" name="${agent['config_prefix']}_enabled" id="${agent['config_prefix']}" value="1" ${agent['state']}> ${agent['name']}--> <!--<input type="checkbox" name="${agent['config_prefix']}_enabled" id="${agent['config_prefix']}" value="1" ${agent['state']}> ${agent['name']}-->
<i class="fa fa-play notify-toggle-icon" data-toggle="tooltip" data-placement="top" title data-title="Notify on playback start" data-id="${agent['id']}" data-config-name="${agent['config_prefix']}_on_play" data-config-value="${agent['on_play']}"></i> <a class="toggle-left notify-toggle-icon" href="javascript:void(0)" data-toggle="tooltip" data-placement="top" title data-title="Notify on playback start" data-id="${agent['id']}" data-config-name="${agent['config_prefix']}_on_play" data-config-value="${agent['on_play']}"><i class="fa fa-play"></i></a>
<i class="fa fa-stop notify-toggle-icon" data-toggle="tooltip" data-placement="top" title data-title="Notify on stop" data-id="${agent['id']}" data-config-name="${agent['config_prefix']}_on_stop" data-config-value="${agent['on_stop']}"></i> <a class="toggle-left notify-toggle-icon" href="javascript:void(0)" data-toggle="tooltip" data-placement="top" title data-title="Notify on stop" data-id="${agent['id']}" data-config-name="${agent['config_prefix']}_on_stop" data-config-value="${agent['on_stop']}"><i class="fa fa-stop"></i></a>
<!--<i class="fa fa-eye notify-toggle-icon" data-toggle="tooltip" data-placement="top" title data-title="Notify on watched" data-id="${agent['id']}" data-config-name="${agent['config_prefix']}_on_watched" data-config-value="${agent['on_watched']}"></i>--> <a class="toggle-left notify-toggle-icon" href="javascript:void(0)" data-toggle="tooltip" data-placement="top" title data-title="Notify on watched" data-id="${agent['id']}" data-config-name="${agent['config_prefix']}_on_watched" data-config-value="${agent['on_watched']}"><i class="fa fa-eye"></i></a>
${agent['name']} ${agent['name']}
% if agent['has_config']: % if agent['has_config']:
<a href="#notification-config-modal" rel="tooltip" data-placement="top" title data-title="Open configuration" data-id="${agent['id']}" class="toggle-notification-config-modal" data-toggle="modal"><i class="fa fa-lg fa-cog"></i></a> <a href="#notification-config-modal" rel="tooltip" data-placement="top" title data-title="Open configuration" data-id="${agent['id']}" class="toggle-notification-config-modal toggle-right" data-toggle="modal"><i class="fa fa-lg fa-cog"></i></a>
% endif % endif
</span> </span>
</li> </li>
@ -661,10 +673,7 @@
$('.notify-toggle-icon').each(function() { $('.notify-toggle-icon').each(function() {
if ($(this).data('config-value') == 1) { if ($(this).data('config-value') == 1) {
$(this).css("color", "#eb8600");
$(this).addClass("active"); $(this).addClass("active");
} else {
$(this).css("color", "#444");
} }
}); });
@ -679,7 +688,6 @@
data: data, data: data,
async: true, async: true,
success: function(data) { success: function(data) {
toggle.css("color", "#444");
toggle.removeClass("active"); toggle.removeClass("active");
} }
}); });
@ -691,7 +699,6 @@
data: data, data: data,
async: true, async: true,
success: function(data) { success: function(data) {
toggle.css("color", "#eb8600");
toggle.addClass("active"); toggle.addClass("active");
} }
}); });

View file

@ -524,6 +524,13 @@ def dbcheck():
'ALTER TABLE session_history_metadata ADD COLUMN full_title TEXT' 'ALTER TABLE session_history_metadata ADD COLUMN full_title TEXT'
) )
# notify_log table :: This is a table which logs notifications sent
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)'
)
conn_db.commit() conn_db.commit()
c_db.close() c_db.close()

View file

@ -103,6 +103,7 @@ _CONFIG_DEFINITIONS = {
'NMA_ON_PLAY': (int, 'NMA', 0), 'NMA_ON_PLAY': (int, 'NMA', 0),
'NMA_ON_STOP': (int, 'NMA', 0), 'NMA_ON_STOP': (int, 'NMA', 0),
'NMA_ON_WATCHED': (int, 'NMA', 0), 'NMA_ON_WATCHED': (int, 'NMA', 0),
'NOTIFY_WATCHED_PERCENT': (int, 'Monitoring', 85),
'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/PlexPy'), 'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/PlexPy'),
'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0), 'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_PLAY': (int, 'OSX_Notify', 0), 'OSX_NOTIFY_ON_PLAY': (int, 'OSX_Notify', 0),
@ -128,6 +129,7 @@ _CONFIG_DEFINITIONS = {
'PUSHALOT_ON_WATCHED': (int, 'Pushalot', 0), 'PUSHALOT_ON_WATCHED': (int, 'Pushalot', 0),
'PUSHBULLET_APIKEY': (str, 'PushBullet', ''), 'PUSHBULLET_APIKEY': (str, 'PushBullet', ''),
'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''), 'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''),
'PUSHBULLET_CHANNEL_TAG': (str, 'PushBullet', ''),
'PUSHBULLET_ENABLED': (int, 'PushBullet', 0), 'PUSHBULLET_ENABLED': (int, 'PushBullet', 0),
'PUSHBULLET_ON_PLAY': (int, 'PushBullet', 0), 'PUSHBULLET_ON_PLAY': (int, 'PushBullet', 0),
'PUSHBULLET_ON_STOP': (int, 'PushBullet', 0), 'PUSHBULLET_ON_STOP': (int, 'PushBullet', 0),

View file

@ -99,7 +99,7 @@ def latinToAscii(unicrap):
pass pass
else: else:
r += str(i) r += str(i)
return r return r.encode('utf-8')
def convert_milliseconds(ms): def convert_milliseconds(ms):
@ -352,7 +352,7 @@ def convert_xml_to_dict(xml):
def get_percent(value1, value2): def get_percent(value1, value2):
if value1.isdigit() and value2.isdigit(): if str(value1).isdigit() and str(value2).isdigit():
value1 = cast_to_float(value1) value1 = cast_to_float(value1)
value2 = cast_to_float(value2) value2 = cast_to_float(value2)
else: else:

View file

@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>. # along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
from plexpy import logger, pmsconnect, notification_handler, log_reader, common, database from plexpy import logger, pmsconnect, notification_handler, log_reader, common, database, helpers
import threading import threading
import plexpy import plexpy
@ -97,8 +97,10 @@ def check_active_sessions():
# Here we can check the play states # Here we can check the play states
if session['state'] != stream['state']: if session['state'] != stream['state']:
if session['state'] == 'paused': if session['state'] == 'paused':
# Push any notifications # Push any notifications -
notification_handler.notify(stream_data=stream, notify_action='pause') # 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 stream['state'] == 'paused': if stream['state'] == 'paused':
# The stream is still paused so we need to increment the paused_counter # 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 # Using the set config parameter as the interval, probably not the most accurate but
@ -107,14 +109,33 @@ def check_active_sessions():
monitor_db.action('UPDATE sessions SET paused_counter = ? ' monitor_db.action('UPDATE sessions SET paused_counter = ? '
'WHERE session_key = ? AND rating_key = ?', 'WHERE session_key = ? AND rating_key = ?',
[paused_counter, stream['session_key'], stream['rating_key']]) [paused_counter, stream['session_key'], stream['rating_key']])
# Check if the user has reached the offset in the media we defined as the "watched" percent
if session['progress'] and session['duration']:
if helpers.get_percent(session['progress'], session['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
threading.Thread(target=notification_handler.notify,
kwargs=dict(stream_data=stream, notify_action='watched')).start()
else: else:
# The user has stopped playing a stream # The user has stopped playing a stream
logger.debug(u"PlexPy Monitor :: Removing sessionKey %s ratingKey %s from session queue" logger.debug(u"PlexPy Monitor :: Removing sessionKey %s ratingKey %s from session queue"
% (stream['session_key'], stream['rating_key'])) % (stream['session_key'], stream['rating_key']))
monitor_db.action('DELETE FROM sessions WHERE session_key = ? AND rating_key = ?', monitor_db.action('DELETE FROM sessions WHERE session_key = ? AND rating_key = ?',
[stream['session_key'], stream['rating_key']]) [stream['session_key'], stream['rating_key']])
# Push any notifications
notification_handler.notify(stream_data=stream, notify_action='stop') # 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
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()
# Write the item history on playback stop # Write the item history on playback stop
monitor_process.write_session_history(session=stream) monitor_process.write_session_history(session=stream)
@ -132,7 +153,8 @@ class MonitorProcessing(object):
def write_session(self, session=None): def write_session(self, session=None):
values = {'rating_key': session['rating_key'], values = {'session_key': session['session_key'],
'rating_key': session['rating_key'],
'media_type': session['type'], 'media_type': session['type'],
'state': session['state'], 'state': session['state'],
'user_id': session['user_id'], 'user_id': session['user_id'],
@ -175,8 +197,10 @@ class MonitorProcessing(object):
result = self.db.upsert('sessions', values, keys) result = self.db.upsert('sessions', values, keys)
if result == 'insert': if result == 'insert':
# Push any notifications # Push any notifications - Push it on it's own thread so we don't hold up our db actions
notification_handler.notify(stream_data=values, notify_action='play') threading.Thread(target=notification_handler.notify,
kwargs=dict(stream_data=values,notify_action='play')).start()
started = int(time.time()) started = int(time.time())
# Try and grab IP address from logs # Try and grab IP address from logs

View file

@ -13,9 +13,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>. # along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
from plexpy import logger, config, notifiers from plexpy import logger, config, notifiers, database
import plexpy import plexpy
import time
def notify(stream_data=None, notify_action=None): def notify(stream_data=None, notify_action=None):
from plexpy import pmsconnect, common from plexpy import pmsconnect, common
@ -37,7 +38,7 @@ def notify(stream_data=None, notify_action=None):
item_title = stream_data['title'] item_title = stream_data['title']
if notify_action == 'play': if notify_action == 'play':
logger.info('PlexPy Monitor :: %s (%s) started playing %s.' % (stream_data['friendly_name'], logger.info('PlexPy Notifier :: %s (%s) started playing %s.' % (stream_data['friendly_name'],
stream_data['player'], item_title)) stream_data['player'], item_title))
if stream_data['media_type'] == 'movie' or stream_data['media_type'] == 'episode': if stream_data['media_type'] == 'movie' or stream_data['media_type'] == 'episode':
@ -45,35 +46,101 @@ def notify(stream_data=None, notify_action=None):
for agent in notifiers.available_notification_agents(): for agent in notifiers.available_notification_agents():
if agent['on_play'] and notify_action == 'play': if agent['on_play'] and notify_action == 'play':
logger.debug("%s agent is configured to notify on playback start." % agent['name']) logger.debug("PlexPy Notifier :: %s agent is configured to notify on playback start." % agent['name'])
message = '%s (%s) started playing %s.' % \ message = '%s (%s) started playing %s.' % \
(stream_data['friendly_name'], stream_data['player'], item_title) (stream_data['friendly_name'], stream_data['player'], item_title)
notifiers.send_notification(config_id=agent['id'], subject=notify_header, body=message) notifiers.send_notification(config_id=agent['id'], subject=notify_header, body=message)
set_notify_state(session=stream_data, state='play', agent_info=agent)
elif agent['on_stop'] and notify_action == 'stop': elif agent['on_stop'] and notify_action == 'stop':
logger.debug("%s agent is configured to notify on playback stop." % agent['name']) logger.debug("PlexPy Notifier :: %s agent is configured to notify on playback stop." % agent['name'])
message = '%s (%s) has stopped %s.' % \ message = '%s (%s) has stopped %s.' % \
(stream_data['friendly_name'], stream_data['player'], item_title) (stream_data['friendly_name'], stream_data['player'], item_title)
notifiers.send_notification(config_id=agent['id'], subject=notify_header, body=message) notifiers.send_notification(config_id=agent['id'], subject=notify_header, body=message)
set_notify_state(session=stream_data, state='stop', agent_info=agent)
elif agent['on_watched'] and notify_action == 'watched':
notify_states = get_notify_state(session=stream_data)
# 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'] for d in notify_states):
logger.debug("PlexPy Notifier :: %s agent is configured to notify on watched." % agent['name'])
message = '%s (%s) has watched %s.' % \
(stream_data['friendly_name'], stream_data['player'], item_title)
notifiers.send_notification(config_id=agent['id'], subject=notify_header, body=message)
set_notify_state(session=stream_data, state='watched', agent_info=agent)
else:
# Check in our notify log if the notification has already been sent
for notify_state in notify_states:
if not notify_state['on_watched'] and (notify_state['agent_id'] == agent['id']):
logger.debug("PlexPy Notifier :: %s agent is configured to notify on watched." % agent['name'])
message = '%s (%s) has watched %s.' % \
(stream_data['friendly_name'], stream_data['player'], item_title)
notifiers.send_notification(config_id=agent['id'], subject=notify_header, body=message)
set_notify_state(session=stream_data, state='watched', agent_info=agent)
elif stream_data['media_type'] == 'track': elif stream_data['media_type'] == 'track':
if plexpy.CONFIG.MUSIC_NOTIFY_ENABLE: if plexpy.CONFIG.MUSIC_NOTIFY_ENABLE:
for agent in notifiers.available_notification_agents(): for agent in notifiers.available_notification_agents():
if agent['on_play'] and notify_action == 'play': if agent['on_play'] and notify_action == 'play':
logger.debug("%s agent is configured to notify on playback start." % agent['name']) logger.debug("PlexPy Notifier :: %s agent is configured to notify on playback start." % agent['name'])
message = '%s (%s) started playing %s.' % \ message = '%s (%s) started playing %s.' % \
(stream_data['friendly_name'], stream_data['player'], item_title) (stream_data['friendly_name'], stream_data['player'], item_title)
notifiers.send_notification(config_id=agent['id'], subject=notify_header, body=message) notifiers.send_notification(config_id=agent['id'], subject=notify_header, body=message)
set_notify_state(session=stream_data, state='play', agent_info=agent)
elif agent['on_stop'] and notify_action == 'stop': elif agent['on_stop'] and notify_action == 'stop':
logger.debug("%s agent is configured to notify on playback stop." % agent['name']) logger.debug("PlexPy Notifier :: %s agent is configured to notify on playback stop." % agent['name'])
message = '%s (%s) has stopped %s.' % \ message = '%s (%s) has stopped %s.' % \
(stream_data['friendly_name'], stream_data['player'], item_title) (stream_data['friendly_name'], stream_data['player'], item_title)
notifiers.send_notification(config_id=agent['id'], subject=notify_header, body=message) notifiers.send_notification(config_id=agent['id'], subject=notify_header, body=message)
set_notify_state(session=stream_data, state='stop', agent_info=agent)
elif stream_data['media_type'] == 'clip': elif stream_data['media_type'] == 'clip':
pass pass
else: else:
logger.debug(u"PlexPy Monitor :: Notify called with unsupported media type.") logger.debug(u"PlexPy Notifier :: Notify called with unsupported media type.")
pass pass
else: else:
logger.debug(u"PlexPy Monitor :: Notify called but incomplete data received.") logger.debug(u"PlexPy Notifier :: Notify called but incomplete data received.")
def get_notify_state(session):
monitor_db = database.MonitorDatabase()
result = monitor_db.select('SELECT on_play, on_stop, on_watched, agent_id '
'FROM notify_log '
'WHERE session_key = ? '
'AND rating_key = ? '
'AND user = ? '
'ORDER BY id DESC',
args=[session['session_key'], session['rating_key'], session['user']])
notify_states = []
for item in result:
notify_state = {'on_play': item[0],
'on_stop': item[1],
'on_watched': item[2],
'agent_id': item[3]}
notify_states.append(notify_state)
return notify_states
def set_notify_state(session, state, agent_info):
if session and state and agent_info:
monitor_db = database.MonitorDatabase()
if state == 'play':
values = {'on_play': int(time.time())}
elif state == 'stop':
values = {'on_stop': int(time.time())}
elif state == 'watched':
values = {'on_watched': int(time.time())}
else:
return
keys = {'session_key': session['session_key'],
'rating_key': session['rating_key'],
'user_id': session['user_id'],
'user': session['user'],
'agent_id': agent_info['id'],
'agent_name': agent_info['name']}
monitor_db.upsert(table_name='notify_log', key_dict=keys, value_dict=values)
else:
logger.error('PlexPy Notifier :: Unable to set notify state.')

View file

@ -44,9 +44,9 @@ AGENT_IDS = {"Growl": 0,
"XBMC": 2, "XBMC": 2,
"Plex": 3, "Plex": 3,
"NMA": 4, "NMA": 4,
"PushAlot": 5, "Pushalot": 5,
"PushBullet": 6, "Pushbullet": 6,
"PushOver": 7, "Pushover": 7,
"OSX Notify": 8, "OSX Notify": 8,
"Boxcar2": 9, "Boxcar2": 9,
"Email": 10} "Email": 10}
@ -97,8 +97,8 @@ def available_notification_agents():
'on_stop': plexpy.CONFIG.NMA_ON_STOP, 'on_stop': plexpy.CONFIG.NMA_ON_STOP,
'on_watched': plexpy.CONFIG.NMA_ON_WATCHED 'on_watched': plexpy.CONFIG.NMA_ON_WATCHED
}, },
{'name': 'PushAlot', {'name': 'Pushalot',
'id': AGENT_IDS['PushAlot'], 'id': AGENT_IDS['Pushalot'],
'config_prefix': 'pushalot', 'config_prefix': 'pushalot',
'has_config': True, 'has_config': True,
'state': checked(plexpy.CONFIG.PUSHALOT_ENABLED), 'state': checked(plexpy.CONFIG.PUSHALOT_ENABLED),
@ -106,8 +106,8 @@ def available_notification_agents():
'on_stop': plexpy.CONFIG.PUSHALOT_ON_STOP, 'on_stop': plexpy.CONFIG.PUSHALOT_ON_STOP,
'on_watched': plexpy.CONFIG.PUSHALOT_ON_WATCHED 'on_watched': plexpy.CONFIG.PUSHALOT_ON_WATCHED
}, },
{'name': 'PushBullet', {'name': 'Pushbullet',
'id': AGENT_IDS['PushBullet'], 'id': AGENT_IDS['Pushbullet'],
'config_prefix': 'pushbullet', 'config_prefix': 'pushbullet',
'has_config': True, 'has_config': True,
'state': checked(plexpy.CONFIG.PUSHBULLET_ENABLED), 'state': checked(plexpy.CONFIG.PUSHBULLET_ENABLED),
@ -115,8 +115,8 @@ def available_notification_agents():
'on_stop': plexpy.CONFIG.PUSHBULLET_ON_STOP, 'on_stop': plexpy.CONFIG.PUSHBULLET_ON_STOP,
'on_watched': plexpy.CONFIG.PUSHBULLET_ON_WATCHED 'on_watched': plexpy.CONFIG.PUSHBULLET_ON_WATCHED
}, },
{'name': 'PushOver', {'name': 'Pushover',
'id': AGENT_IDS['PushOver'], 'id': AGENT_IDS['Pushover'],
'config_prefix': 'pushover', 'config_prefix': 'pushover',
'has_config': True, 'has_config': True,
'state': checked(plexpy.CONFIG.PUSHOVER_ENABLED), 'state': checked(plexpy.CONFIG.PUSHOVER_ENABLED),
@ -257,7 +257,7 @@ class GROWL(object):
return cherrypy.config['config'].get('Growl', options) return cherrypy.config['config'].get('Growl', options)
def notify(self, message, event): def notify(self, message, event):
if not self.enabled: if not message or not event:
return return
# Split host and port # Split host and port
@ -362,7 +362,7 @@ class PROWL(object):
return cherrypy.config['config'].get('Prowl', options) return cherrypy.config['config'].get('Prowl', options)
def notify(self, message, event): def notify(self, message, event):
if not plexpy.CONFIG.PROWL_ENABLED: if not message or not event:
return return
http_handler = HTTPSConnection("api.prowlapp.com") http_handler = HTTPSConnection("api.prowlapp.com")
@ -596,18 +596,21 @@ class NMA(object):
self.on_watched = plexpy.CONFIG.NMA_ON_WATCHED self.on_watched = plexpy.CONFIG.NMA_ON_WATCHED
def notify(self, subject=None, message=None): def notify(self, subject=None, message=None):
if not subject or not message:
return
title = 'PlexPy' title = 'PlexPy'
api = plexpy.CONFIG.NMA_APIKEY api = plexpy.CONFIG.NMA_APIKEY
nma_priority = plexpy.CONFIG.NMA_PRIORITY nma_priority = plexpy.CONFIG.NMA_PRIORITY
logger.debug(u"NMA title: " + title) # logger.debug(u"NMA title: " + title)
logger.debug(u"NMA API: " + api) # logger.debug(u"NMA API: " + api)
logger.debug(u"NMA Priority: " + str(nma_priority)) # logger.debug(u"NMA Priority: " + str(nma_priority))
event = subject event = subject
logger.debug(u"NMA event: " + event) # logger.debug(u"NMA event: " + event)
logger.debug(u"NMA message: " + message) # logger.debug(u"NMA message: " + message)
batch = False batch = False
@ -648,6 +651,7 @@ class PUSHBULLET(object):
def __init__(self): def __init__(self):
self.apikey = plexpy.CONFIG.PUSHBULLET_APIKEY self.apikey = plexpy.CONFIG.PUSHBULLET_APIKEY
self.deviceid = plexpy.CONFIG.PUSHBULLET_DEVICEID self.deviceid = plexpy.CONFIG.PUSHBULLET_DEVICEID
self.channel_tag = plexpy.CONFIG.PUSHBULLET_CHANNEL_TAG
self.on_play = plexpy.CONFIG.PUSHBULLET_ON_PLAY self.on_play = plexpy.CONFIG.PUSHBULLET_ON_PLAY
self.on_stop = plexpy.CONFIG.PUSHBULLET_ON_STOP self.on_stop = plexpy.CONFIG.PUSHBULLET_ON_STOP
self.on_watched = plexpy.CONFIG.PUSHBULLET_ON_WATCHED self.on_watched = plexpy.CONFIG.PUSHBULLET_ON_WATCHED
@ -665,16 +669,22 @@ class PUSHBULLET(object):
'title': subject.encode("utf-8"), 'title': subject.encode("utf-8"),
'body': message.encode("utf-8")} 'body': message.encode("utf-8")}
# Can only send to a device or channel, not both.
if self.deviceid:
data['device_iden'] = self.deviceid
elif self.channel_tag:
data['channel_tag'] = self.channel_tag
http_handler.request("POST", http_handler.request("POST",
"/v2/pushes", "/v2/pushes",
headers={'Content-type': "application/json", headers={'Content-type': "application/json",
'Authorization': 'Basic %s' % base64.b64encode(plexpy.CONFIG.PUSHBULLET_APIKEY + ":")}, 'Authorization': 'Basic %s' % base64.b64encode(plexpy.CONFIG.PUSHBULLET_APIKEY + ":")},
body=json.dumps(data)) body=json.dumps(data))
response = http_handler.getresponse() response = http_handler.getresponse()
request_status = response.status request_status = response.status
logger.debug(u"PushBullet response status: %r" % request_status) # logger.debug(u"PushBullet response status: %r" % request_status)
logger.debug(u"PushBullet response headers: %r" % response.getheaders()) # logger.debug(u"PushBullet response headers: %r" % response.getheaders())
logger.debug(u"PushBullet response body: %r" % response.read()) # logger.debug(u"PushBullet response body: %r" % response.read())
if request_status == 200: if request_status == 200:
logger.info(u"PushBullet notifications sent.") logger.info(u"PushBullet notifications sent.")
@ -704,7 +714,13 @@ class PUSHBULLET(object):
{'label': 'Device ID', {'label': 'Device ID',
'value': self.deviceid, 'value': self.deviceid,
'name': 'pushbullet_deviceid', 'name': 'pushbullet_deviceid',
'description': 'A device ID (optional).', 'description': 'A device ID (optional). If set, will override channel tag.',
'input_type': 'text'
},
{'label': 'Channel',
'value': self.channel_tag,
'name': 'pushbullet_channel_tag',
'description': 'A channel tag (optional).',
'input_type': 'text' 'input_type': 'text'
} }
] ]
@ -720,7 +736,7 @@ class PUSHALOT(object):
self.on_watched = plexpy.CONFIG.PUSHALOT_ON_WATCHED self.on_watched = plexpy.CONFIG.PUSHALOT_ON_WATCHED
def notify(self, message, event): def notify(self, message, event):
if not plexpy.CONFIG.PUSHALOT_ENABLED: if not message or not event:
return return
pushalot_authorizationtoken = plexpy.CONFIG.PUSHALOT_APIKEY pushalot_authorizationtoken = plexpy.CONFIG.PUSHALOT_APIKEY
@ -786,7 +802,7 @@ class PUSHOVER(object):
return cherrypy.config['config'].get('Pushover', options) return cherrypy.config['config'].get('Pushover', options)
def notify(self, message, event): def notify(self, message, event):
if not plexpy.CONFIG.PUSHOVER_ENABLED: if not message or not event:
return return
http_handler = HTTPSConnection("api.pushover.net") http_handler = HTTPSConnection("api.pushover.net")
@ -1037,11 +1053,11 @@ class BOXCAR(object):
self.on_stop = plexpy.CONFIG.BOXCAR_ON_STOP self.on_stop = plexpy.CONFIG.BOXCAR_ON_STOP
self.on_watched = plexpy.CONFIG.BOXCAR_ON_WATCHED self.on_watched = plexpy.CONFIG.BOXCAR_ON_WATCHED
def notify(self, title, message, rgid=None): def notify(self, title, message):
try: if not title or not message:
if rgid: return
message += '<br></br><a href="http://musicbrainz.org/release-group/%s">MusicBrainz</a>' % rgid
try:
data = urllib.urlencode({ data = urllib.urlencode({
'user_credentials': plexpy.CONFIG.BOXCAR_TOKEN, 'user_credentials': plexpy.CONFIG.BOXCAR_TOKEN,
'notification[title]': title.encode('utf-8'), 'notification[title]': title.encode('utf-8'),
@ -1077,6 +1093,8 @@ class Email(object):
self.on_watched = plexpy.CONFIG.EMAIL_ON_WATCHED self.on_watched = plexpy.CONFIG.EMAIL_ON_WATCHED
def notify(self, subject, message): def notify(self, subject, message):
if not subject or not message:
return
message = MIMEText(message, 'plain', "utf-8") message = MIMEText(message, 'plain', "utf-8")
message['Subject'] = subject message['Subject'] = subject

View file

@ -510,7 +510,8 @@ class WebInterface(object):
"video_logging_enable": checked(plexpy.CONFIG.VIDEO_LOGGING_ENABLE), "video_logging_enable": checked(plexpy.CONFIG.VIDEO_LOGGING_ENABLE),
"music_logging_enable": checked(plexpy.CONFIG.MUSIC_LOGGING_ENABLE), "music_logging_enable": checked(plexpy.CONFIG.MUSIC_LOGGING_ENABLE),
"logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL, "logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
"pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE) "pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE),
"notify_watched_percent": plexpy.CONFIG.NOTIFY_WATCHED_PERCENT
} }
return serve_template(templatename="settings.html", title="Settings", config=config) return serve_template(templatename="settings.html", title="Settings", config=config)