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;
}
.stacked-configs > li > span > a {
.stacked-configs > li > span > a.toggle-right {
float: right;
color: #999;
padding-left: 10px;
}
.stacked-configs > li > span > a.toggle-left {
color: #444;
padding-right: 2px;
}
.stacked-configs > li > span > a:hover {
color: #eee;
}
.stacked-configs > li > span > i {
cursor: pointer;
padding-right: 2px;
color: #444;
}
.stacked-configs > li > span > i:hover {
color: #eee;
.stacked-configs > li > span > a.active {
color: #eb8600;
}
.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>
<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']}
% endif
</div>
<div class="dashboard-activity-metadata-title">
% if a['type'] == 'episode':

View file

@ -80,6 +80,7 @@
<div class="wellheader">
<h3>Web Interface</h3>
</div>
<p class="help-block">Web interface changes require a restart.</p>
<div class="form-group">
<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>
@ -116,6 +117,7 @@
<div class="wellheader">
<h3>Authentication</h3>
</div>
<p class="help-block">Authentication changes require a restart.</p>
<div class="form-group">
<label for="http_username">HTTP Username</label>
<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">
<fieldset>
<div class="wellheader">
<h3>Global Notifications</h3>
<h3>Global Notification Toggles</h3>
</div>
<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
@ -282,6 +284,16 @@
<input type="checkbox" name="music_notify_enable" id="music_notify_enable" value="1" ${config['music_notify_enable']}> Enable Music Notifications
</div>
</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">
</div>
<div role="tabpanel" class="tab-pane" id="tabs-9">
@ -299,12 +311,12 @@
<li>
<span>
<!--<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>
<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>
<!--<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 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>
<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>
<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']}
% 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
</span>
</li>
@ -661,10 +673,7 @@
$('.notify-toggle-icon').each(function() {
if ($(this).data('config-value') == 1) {
$(this).css("color", "#eb8600");
$(this).addClass("active");
} else {
$(this).css("color", "#444");
}
});
@ -679,7 +688,6 @@
data: data,
async: true,
success: function(data) {
toggle.css("color", "#444");
toggle.removeClass("active");
}
});
@ -691,7 +699,6 @@
data: data,
async: true,
success: function(data) {
toggle.css("color", "#eb8600");
toggle.addClass("active");
}
});

View file

@ -524,6 +524,13 @@ def dbcheck():
'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()
c_db.close()

View file

@ -103,6 +103,7 @@ _CONFIG_DEFINITIONS = {
'NMA_ON_PLAY': (int, 'NMA', 0),
'NMA_ON_STOP': (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_ENABLED': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_PLAY': (int, 'OSX_Notify', 0),
@ -128,6 +129,7 @@ _CONFIG_DEFINITIONS = {
'PUSHALOT_ON_WATCHED': (int, 'Pushalot', 0),
'PUSHBULLET_APIKEY': (str, 'PushBullet', ''),
'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''),
'PUSHBULLET_CHANNEL_TAG': (str, 'PushBullet', ''),
'PUSHBULLET_ENABLED': (int, 'PushBullet', 0),
'PUSHBULLET_ON_PLAY': (int, 'PushBullet', 0),
'PUSHBULLET_ON_STOP': (int, 'PushBullet', 0),

View file

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

View file

@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License
# 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 plexpy
@ -97,8 +97,10 @@ def check_active_sessions():
# Here we can check the play states
if session['state'] != stream['state']:
if session['state'] == 'paused':
# Push any notifications
notification_handler.notify(stream_data=stream, notify_action='pause')
# 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 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
@ -107,14 +109,33 @@ 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']])
# 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:
# 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']])
# 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
monitor_process.write_session_history(session=stream)
@ -132,7 +153,8 @@ class MonitorProcessing(object):
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'],
'state': session['state'],
'user_id': session['user_id'],
@ -175,8 +197,10 @@ class MonitorProcessing(object):
result = self.db.upsert('sessions', values, keys)
if result == 'insert':
# Push any notifications
notification_handler.notify(stream_data=values, notify_action='play')
# 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()
started = int(time.time())
# 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
# 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 time
def notify(stream_data=None, notify_action=None):
from plexpy import pmsconnect, common
@ -37,7 +38,7 @@ def notify(stream_data=None, notify_action=None):
item_title = stream_data['title']
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))
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():
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.' % \
(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='play', agent_info=agent)
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.' % \
(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='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':
if plexpy.CONFIG.MUSIC_NOTIFY_ENABLE:
for agent in notifiers.available_notification_agents():
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.' % \
(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='play', agent_info=agent)
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.' % \
(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='stop', agent_info=agent)
elif stream_data['media_type'] == 'clip':
pass
else:
logger.debug(u"PlexPy Monitor :: Notify called with unsupported media type.")
logger.debug(u"PlexPy Notifier :: Notify called with unsupported media type.")
pass
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,
"Plex": 3,
"NMA": 4,
"PushAlot": 5,
"PushBullet": 6,
"PushOver": 7,
"Pushalot": 5,
"Pushbullet": 6,
"Pushover": 7,
"OSX Notify": 8,
"Boxcar2": 9,
"Email": 10}
@ -97,8 +97,8 @@ def available_notification_agents():
'on_stop': plexpy.CONFIG.NMA_ON_STOP,
'on_watched': plexpy.CONFIG.NMA_ON_WATCHED
},
{'name': 'PushAlot',
'id': AGENT_IDS['PushAlot'],
{'name': 'Pushalot',
'id': AGENT_IDS['Pushalot'],
'config_prefix': 'pushalot',
'has_config': True,
'state': checked(plexpy.CONFIG.PUSHALOT_ENABLED),
@ -106,8 +106,8 @@ def available_notification_agents():
'on_stop': plexpy.CONFIG.PUSHALOT_ON_STOP,
'on_watched': plexpy.CONFIG.PUSHALOT_ON_WATCHED
},
{'name': 'PushBullet',
'id': AGENT_IDS['PushBullet'],
{'name': 'Pushbullet',
'id': AGENT_IDS['Pushbullet'],
'config_prefix': 'pushbullet',
'has_config': True,
'state': checked(plexpy.CONFIG.PUSHBULLET_ENABLED),
@ -115,8 +115,8 @@ def available_notification_agents():
'on_stop': plexpy.CONFIG.PUSHBULLET_ON_STOP,
'on_watched': plexpy.CONFIG.PUSHBULLET_ON_WATCHED
},
{'name': 'PushOver',
'id': AGENT_IDS['PushOver'],
{'name': 'Pushover',
'id': AGENT_IDS['Pushover'],
'config_prefix': 'pushover',
'has_config': True,
'state': checked(plexpy.CONFIG.PUSHOVER_ENABLED),
@ -257,7 +257,7 @@ class GROWL(object):
return cherrypy.config['config'].get('Growl', options)
def notify(self, message, event):
if not self.enabled:
if not message or not event:
return
# Split host and port
@ -362,7 +362,7 @@ class PROWL(object):
return cherrypy.config['config'].get('Prowl', options)
def notify(self, message, event):
if not plexpy.CONFIG.PROWL_ENABLED:
if not message or not event:
return
http_handler = HTTPSConnection("api.prowlapp.com")
@ -596,18 +596,21 @@ class NMA(object):
self.on_watched = plexpy.CONFIG.NMA_ON_WATCHED
def notify(self, subject=None, message=None):
if not subject or not message:
return
title = 'PlexPy'
api = plexpy.CONFIG.NMA_APIKEY
nma_priority = plexpy.CONFIG.NMA_PRIORITY
logger.debug(u"NMA title: " + title)
logger.debug(u"NMA API: " + api)
logger.debug(u"NMA Priority: " + str(nma_priority))
# logger.debug(u"NMA title: " + title)
# logger.debug(u"NMA API: " + api)
# logger.debug(u"NMA Priority: " + str(nma_priority))
event = subject
logger.debug(u"NMA event: " + event)
logger.debug(u"NMA message: " + message)
# logger.debug(u"NMA event: " + event)
# logger.debug(u"NMA message: " + message)
batch = False
@ -648,6 +651,7 @@ class PUSHBULLET(object):
def __init__(self):
self.apikey = plexpy.CONFIG.PUSHBULLET_APIKEY
self.deviceid = plexpy.CONFIG.PUSHBULLET_DEVICEID
self.channel_tag = plexpy.CONFIG.PUSHBULLET_CHANNEL_TAG
self.on_play = plexpy.CONFIG.PUSHBULLET_ON_PLAY
self.on_stop = plexpy.CONFIG.PUSHBULLET_ON_STOP
self.on_watched = plexpy.CONFIG.PUSHBULLET_ON_WATCHED
@ -665,6 +669,12 @@ class PUSHBULLET(object):
'title': subject.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",
"/v2/pushes",
headers={'Content-type': "application/json",
@ -672,9 +682,9 @@ class PUSHBULLET(object):
body=json.dumps(data))
response = http_handler.getresponse()
request_status = response.status
logger.debug(u"PushBullet response status: %r" % request_status)
logger.debug(u"PushBullet response headers: %r" % response.getheaders())
logger.debug(u"PushBullet response body: %r" % response.read())
# logger.debug(u"PushBullet response status: %r" % request_status)
# logger.debug(u"PushBullet response headers: %r" % response.getheaders())
# logger.debug(u"PushBullet response body: %r" % response.read())
if request_status == 200:
logger.info(u"PushBullet notifications sent.")
@ -704,7 +714,13 @@ class PUSHBULLET(object):
{'label': 'Device ID',
'value': self.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'
}
]
@ -720,7 +736,7 @@ class PUSHALOT(object):
self.on_watched = plexpy.CONFIG.PUSHALOT_ON_WATCHED
def notify(self, message, event):
if not plexpy.CONFIG.PUSHALOT_ENABLED:
if not message or not event:
return
pushalot_authorizationtoken = plexpy.CONFIG.PUSHALOT_APIKEY
@ -786,7 +802,7 @@ class PUSHOVER(object):
return cherrypy.config['config'].get('Pushover', options)
def notify(self, message, event):
if not plexpy.CONFIG.PUSHOVER_ENABLED:
if not message or not event:
return
http_handler = HTTPSConnection("api.pushover.net")
@ -1037,11 +1053,11 @@ class BOXCAR(object):
self.on_stop = plexpy.CONFIG.BOXCAR_ON_STOP
self.on_watched = plexpy.CONFIG.BOXCAR_ON_WATCHED
def notify(self, title, message, rgid=None):
try:
if rgid:
message += '<br></br><a href="http://musicbrainz.org/release-group/%s">MusicBrainz</a>' % rgid
def notify(self, title, message):
if not title or not message:
return
try:
data = urllib.urlencode({
'user_credentials': plexpy.CONFIG.BOXCAR_TOKEN,
'notification[title]': title.encode('utf-8'),
@ -1077,6 +1093,8 @@ class Email(object):
self.on_watched = plexpy.CONFIG.EMAIL_ON_WATCHED
def notify(self, subject, message):
if not subject or not message:
return
message = MIMEText(message, 'plain', "utf-8")
message['Subject'] = subject

View file

@ -510,7 +510,8 @@ class WebInterface(object):
"video_logging_enable": checked(plexpy.CONFIG.VIDEO_LOGGING_ENABLE),
"music_logging_enable": checked(plexpy.CONFIG.MUSIC_LOGGING_ENABLE),
"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)