Compare commits
15 Commits
v2.0.10-be
...
v2.0.12-be
Author | SHA1 | Date | |
---|---|---|---|
![]() |
644fea6665 | ||
![]() |
a1349ff8a6 | ||
![]() |
71c20002b8 | ||
![]() |
157af84226 | ||
![]() |
9b4536f132 | ||
![]() |
29ab470e42 | ||
![]() |
c67fa480a7 | ||
![]() |
0a1a691c73 | ||
![]() |
48588f23bf | ||
![]() |
cf14fbc3f0 | ||
![]() |
e471d5207d | ||
![]() |
5722a52082 | ||
![]() |
08c32e875e | ||
![]() |
7d3ee3afb3 | ||
![]() |
def8600f5c |
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,5 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
## v2.0.12-beta (2018-01-07)
|
||||
|
||||
* Notifications:
|
||||
* Fix: Incorrect Plex URL parameter value.
|
||||
* Change: Custom condition logic is now optional. An implicit "and" is applied between all conditions if the logic is blank.
|
||||
* UI:
|
||||
* New: Added separate required LAN/WAN bandwidth in the activity header.
|
||||
* API:
|
||||
* Fix: Notify API command not sending notifications.
|
||||
|
||||
|
||||
## v2.0.11-beta (2018-01-05)
|
||||
|
||||
* Notifications:
|
||||
* Fix: Some notification parameters showing up blank.
|
||||
* UI:
|
||||
* Fix: Stream data showing up as "None" for pre-v2 history.
|
||||
* Other:
|
||||
* Fix: Ability to login using the hashed password.
|
||||
|
||||
|
||||
## v2.0.10-beta (2018-01-04)
|
||||
|
||||
* Monitoring:
|
||||
|
@@ -71,11 +71,13 @@ select.form-control {
|
||||
border-radius: 3px;
|
||||
transition: background-color .3s;
|
||||
}
|
||||
.react-selectize.root-node .react-selectize-control {
|
||||
.react-selectize.root-node .react-selectize-control,
|
||||
.selectize-control.form-control .selectize-input {
|
||||
color: #fff !important;
|
||||
border: 0px solid #444 !important;
|
||||
background: #555 !important;
|
||||
padding: 1px 2px;
|
||||
transition: background-color .3s;
|
||||
}
|
||||
.react-selectize.root-node .react-selectize-control .react-selectize-placeholder {
|
||||
color: #fff !important;
|
||||
@@ -83,6 +85,13 @@ select.form-control {
|
||||
.react-selectize.root-node .react-selectize-control .react-selectize-toggle-button path {
|
||||
fill: #fff !important;
|
||||
}
|
||||
.react-selectize.root-node .simple-value,
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
background: #444444 !important;
|
||||
color: #ffffff !important;
|
||||
padding-bottom: 2px !important;
|
||||
transition: background-color .3s;
|
||||
}
|
||||
.react-selectize.root-node .simple-value span {
|
||||
padding-bottom: 2px !important;
|
||||
}
|
||||
@@ -90,13 +99,25 @@ select.form-control {
|
||||
padding-top: 3px !important;
|
||||
padding-bottom: 3px !important;
|
||||
}
|
||||
select.form-control:focus {
|
||||
select.form-control:focus,
|
||||
.react-selectize.root-node.open .react-selectize-control,
|
||||
.selectize-control.form-control .selectize-input.focus {
|
||||
outline: 0;
|
||||
outline: thin dotted \9;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
color: #555 !important;
|
||||
background-color: #fff !important;
|
||||
transition: background-color .3s;
|
||||
}
|
||||
.react-selectize.root-node.open .simple-value,
|
||||
.selectize-control.multi .selectize-input.focus > div,
|
||||
.selectize-control.multi .selectize-input > div.active{
|
||||
background: #efefef !important;
|
||||
color: #333333 !important;
|
||||
transition: background-color .3s;
|
||||
}
|
||||
.react-selectize.root-node.open .react-selectize-control .react-selectize-toggle-button path {
|
||||
fill: #999 !important;
|
||||
}
|
||||
select.form-control option {
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
@@ -3722,7 +3743,11 @@ a:hover .overlay-refresh-image:hover {
|
||||
.no-image {
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
#info-modal .stream-info-current {
|
||||
color: #aaa;
|
||||
text-align: center;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
#info-modal .stream-info-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@@ -292,7 +292,9 @@
|
||||
var sc_dp = current_activity.stream_count_direct_play,
|
||||
sc_ds = current_activity.stream_count_direct_stream,
|
||||
sc_tc = current_activity.stream_count_transcode,
|
||||
total_bw = current_activity.total_bandwidth;
|
||||
total_bw = current_activity.total_bandwidth,
|
||||
lan_bw = current_activity.lan_bandwidth,
|
||||
wan_bw = current_activity.wan_bandwidth;
|
||||
var streams_header = stream_count + ' stream' + (stream_count > 1 ? 's' : '') + ' (';
|
||||
if (sc_dp) {
|
||||
streams_header += sc_dp + ' direct play' + (sc_dp > 1 ? 's' : '') + ', ';
|
||||
@@ -306,7 +308,14 @@
|
||||
streams_header = streams_header.replace(/, $/, '') + ')';
|
||||
$('#currentActivityHeader-streams').text(streams_header);
|
||||
|
||||
var bandwidth_header = (total_bw > 1000) ? ((total_bw / 1000).toFixed(1) + ' Mbps') : (total_bw + ' kbps');
|
||||
var bandwidth_header = ((total_bw > 1000) ? ((total_bw / 1000).toFixed(1) + ' Mbps') : (total_bw + ' kbps')) + ' (';
|
||||
if (lan_bw) {
|
||||
bandwidth_header += 'LAN: ' + ((lan_bw > 1000) ? ((lan_bw / 1000).toFixed(1) + ' Mbps') : (lan_bw + ' kbps')) + ', ';
|
||||
}
|
||||
if (wan_bw) {
|
||||
bandwidth_header += 'WAN: ' + ((wan_bw > 1000) ? ((wan_bw / 1000).toFixed(1) + ' Mbps') : (wan_bw + ' kbps')) + ', ';
|
||||
}
|
||||
bandwidth_header = bandwidth_header.replace(/, $/, '') + ')';
|
||||
$('#currentActivityHeader-bandwidth').text(bandwidth_header);
|
||||
|
||||
$('#currentActivityHeader').show();
|
||||
|
@@ -132,12 +132,9 @@
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-notify_conditions">
|
||||
<label>Notification Conditions</label>
|
||||
<p class="help-block">
|
||||
Add custom notification conditions.
|
||||
Add custom conditions to filter out notifications.
|
||||
<a href="#notify-text-sub-modal" data-toggle="modal">Click here</a> for a description of all the parameters.
|
||||
</p>
|
||||
<p class="help-block">
|
||||
Note: Conditions are checked after the notification trigger and the notification will only be sent if the condition logic is satisfied.
|
||||
</p>
|
||||
<div id="condition-widget"></div>
|
||||
<input type="hidden" name="custom_conditions" id="custom_conditions" />
|
||||
|
||||
@@ -146,7 +143,8 @@
|
||||
<input type="text" class="form-control" name="custom_conditions_logic" id="custom_conditions_logic" value="${notifier['custom_conditions_logic']}" required />
|
||||
<div id="custom_conditions_logic_error" class="alert alert-danger" role="alert" style="padding-top: 5px; padding-bottom: 5px; margin: 0; display: none;"><i class="fa fa-exclamation-triangle" style="color: #a94442;"></i> <span></span></div>
|
||||
<p class="help-block">
|
||||
Enter the logic to use when evaluating the conditions (e.g. <span class="inline-pre">{1} and ({2} or {3})</span>).
|
||||
Optional: Enter custom logic to use when evaluating the conditions (e.g. <span class="inline-pre">{1} and ({2} or {3})</span>).
|
||||
Leave blank for implicit <span class="inline-pre">and</span> between all conditions.
|
||||
</p>
|
||||
<p class="help-block">
|
||||
Note: Only the keywords <span class="inline-pre">and</span>/<span class="inline-pre">or</span> and brackets <span class="inline-pre">()</span> are supported.
|
||||
|
@@ -63,7 +63,7 @@ DOCUMENTATION :: END
|
||||
<h3 class="text-muted"> </h3>
|
||||
</div>
|
||||
% elif item['media_type'] == 'show':
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['parent_title']}">
|
||||
<a href="info?rating_key=${item['rating_key']}" title="${item['title']}">
|
||||
<div class="dashboard-recent-media-poster">
|
||||
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
|
||||
<div class="dashboard-recent-media-overlay">
|
||||
|
@@ -918,7 +918,7 @@
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="get_file_sizes" name="get_file_sizes" value="1" ${config['get_file_sizes']}> Calculate Total File Sizes <span style="color: #eb8600; padding-left: 10px;">[experimental]</span>
|
||||
<input type="checkbox" id="get_file_sizes" name="get_file_sizes" value="1" ${config['get_file_sizes']}> Calculate Total File Sizes
|
||||
</label>
|
||||
<p class="help-block">Enable if you want Tautulli to calculate the total file size for TV Shows/Seasons and Artists/Albums on the media info tables.</p>
|
||||
</div>
|
||||
|
@@ -54,6 +54,11 @@ DOCUMENTATION :: END
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
% if data['current_session']:
|
||||
<div class="col-sm-12 text-muted stream-info-current">
|
||||
<i class="fa fa-exclamation-circle"></i> Current session. Updated stream details below may be delayed.
|
||||
</div>
|
||||
% endif
|
||||
<table class="stream-info" style="margin-top: 0;">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@@ -1189,6 +1189,27 @@ def dbcheck():
|
||||
)
|
||||
|
||||
|
||||
# Upgrade session_history_media_info table from earlier versions
|
||||
try:
|
||||
result = c_db.execute('SELECT stream_container FROM session_history_media_info '
|
||||
'WHERE stream_container IS NULL').fetchall()
|
||||
if len(result) > 0:
|
||||
logger.debug(u"Altering database. Removing NULL values from session_history_media_info table.")
|
||||
c_db.execute(
|
||||
'UPDATE session_history_media_info SET stream_container = "" WHERE stream_container IS NULL '
|
||||
)
|
||||
c_db.execute(
|
||||
'UPDATE session_history_media_info SET stream_video_codec = "" WHERE stream_video_codec IS NULL '
|
||||
)
|
||||
c_db.execute(
|
||||
'UPDATE session_history_media_info SET stream_audio_codec = "" WHERE stream_audio_codec IS NULL '
|
||||
)
|
||||
c_db.execute(
|
||||
'UPDATE session_history_media_info SET stream_subtitle_codec = "" WHERE stream_subtitle_codec IS NULL '
|
||||
)
|
||||
except sqlite3.OperationalError:
|
||||
logger.warn(u"Unable to remove NULL values from session_history_media_info table.")
|
||||
|
||||
# Upgrade users table from earlier versions
|
||||
try:
|
||||
c_db.execute('SELECT do_notify FROM users')
|
||||
@@ -1370,8 +1391,8 @@ def dbcheck():
|
||||
|
||||
# Upgrade library_sections table from earlier versions (remove duplicated libraries)
|
||||
try:
|
||||
result = c_db.execute('SELECT * FROM library_sections WHERE server_id = ""')
|
||||
if result.rowcount > 0:
|
||||
result = c_db.execute('SELECT * FROM library_sections WHERE server_id = ""').fetchall()
|
||||
if len(result) > 0:
|
||||
logger.debug(u"Altering database. Removing duplicate libraries from library_sections table.")
|
||||
c_db.execute(
|
||||
'DELETE FROM library_sections WHERE server_id = ""'
|
||||
|
@@ -33,6 +33,7 @@ ACTIVITY_SCHED = BackgroundScheduler()
|
||||
|
||||
RECENTLY_ADDED_QUEUE = {}
|
||||
|
||||
|
||||
class ActivityHandler(object):
|
||||
|
||||
def __init__(self, timeline):
|
||||
@@ -229,6 +230,8 @@ class ActivityHandler(object):
|
||||
# Update the session state and viewOffset
|
||||
if this_state == 'playing':
|
||||
# Update the session in our temp session table
|
||||
# if the last set temporary stopped time exceeds 15 seconds
|
||||
if int(time.time()) - db_session['stopped'] > 60:
|
||||
session = self.get_live_session()
|
||||
if session:
|
||||
self.update_db_session(session=session)
|
||||
|
@@ -35,6 +35,8 @@ import database
|
||||
import libraries
|
||||
import logger
|
||||
import mobile_app
|
||||
import notification_handler
|
||||
import notifiers
|
||||
import users
|
||||
|
||||
|
||||
@@ -397,6 +399,50 @@ class API2:
|
||||
|
||||
return
|
||||
|
||||
def notify(self, notifier_id='', subject='Tautulli', body='Test notification', **kwargs):
|
||||
""" Send a notification using Tautulli.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
notifier_id (int): The ID number of the notification agent
|
||||
subject (str): The subject of the message
|
||||
body (str): The body of the message
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
|
||||
Returns:
|
||||
None
|
||||
```
|
||||
"""
|
||||
if not notifier_id:
|
||||
self._api_msg = 'Notification failed: no notifier id provided.'
|
||||
self._api_result_type = 'error'
|
||||
return
|
||||
|
||||
notifier = notifiers.get_notifier_config(notifier_id=notifier_id)
|
||||
|
||||
if not notifier:
|
||||
self._api_msg = 'Notification failed: invalid notifier_id provided %s.' % notifier_id
|
||||
self._api_result_type = 'error'
|
||||
return
|
||||
|
||||
logger.api_debug(u'Tautulli APIv2 :: Sending notification.')
|
||||
success = notification_handler.notify(notifier_id=notifier_id,
|
||||
notify_action='api',
|
||||
subject=subject,
|
||||
body=body,
|
||||
**kwargs)
|
||||
|
||||
if success:
|
||||
self._api_msg = 'Notification sent.'
|
||||
self._api_result_type = 'success'
|
||||
else:
|
||||
self._api_msg = 'Notification failed.'
|
||||
self._api_result_type = 'error'
|
||||
|
||||
return
|
||||
|
||||
def _api_make_md(self):
|
||||
""" Tries to make a API.md to simplify the api docs. """
|
||||
|
||||
@@ -581,8 +627,8 @@ General optional parameters:
|
||||
if isinstance(result, (dict, list)):
|
||||
ret = result
|
||||
else:
|
||||
raise
|
||||
except:
|
||||
raise Exception
|
||||
except Exception:
|
||||
try:
|
||||
ret = json.loads(result)
|
||||
except (ValueError, TypeError):
|
||||
|
@@ -289,6 +289,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'LOG_BLACKLIST': (int, 'General', 1),
|
||||
'LOG_DIR': (str, 'General', ''),
|
||||
'LOGGING_IGNORE_INTERVAL': (int, 'Monitoring', 120),
|
||||
'METADATA_CACHE_SECONDS': (int, 'Advanced', 1800),
|
||||
'MOVIE_LOGGING_ENABLE': (int, 'Monitoring', 1),
|
||||
'MOVIE_NOTIFY_ENABLE': (int, 'Monitoring', 0),
|
||||
'MOVIE_NOTIFY_ON_START': (int, 'Monitoring', 1),
|
||||
|
@@ -951,9 +951,11 @@ class DataFactory(object):
|
||||
'transcode_hw_encoding': item['transcode_hw_encoding'],
|
||||
'media_type': item['media_type'],
|
||||
'title': item['title'],
|
||||
'grandparent_title': item['grandparent_title']
|
||||
'grandparent_title': item['grandparent_title'],
|
||||
'current_session': 1 if session_key else 0
|
||||
}
|
||||
|
||||
stream_output = {k: v or '' for k, v in stream_output.iteritems()}
|
||||
return stream_output
|
||||
|
||||
def get_metadata_details(self, rating_key):
|
||||
|
@@ -206,12 +206,14 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
|
||||
notifier_config = notifiers.get_notifier_config(notifier_id=notifier_id)
|
||||
|
||||
custom_conditions_logic = notifier_config['custom_conditions_logic']
|
||||
custom_conditions = json.loads(notifier_config['custom_conditions']) or []
|
||||
|
||||
if custom_conditions_logic or any(c for c in custom_conditions if c['value']):
|
||||
logger.debug(u"Tautulli NotificationHandler :: Checking custom notification conditions for notifier_id %s."
|
||||
% notifier_id)
|
||||
|
||||
logic_groups = None
|
||||
if custom_conditions_logic:
|
||||
logger.debug(u"Tautulli NotificationHandler :: Checking custom notification conditions for notifier_id %s." % notifier_id)
|
||||
|
||||
custom_conditions = json.loads(notifier_config['custom_conditions'])
|
||||
|
||||
try:
|
||||
# Parse and validate the custom conditions logic
|
||||
logic_groups = helpers.parse_condition_logic_string(custom_conditions_logic, len(custom_conditions))
|
||||
@@ -227,10 +229,11 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
|
||||
operator = condition['operator']
|
||||
values = condition['value']
|
||||
parameter_type = condition['type']
|
||||
parameter_value = parameters.get(parameter, "")
|
||||
|
||||
# Set blank conditions to None
|
||||
# Set blank conditions to True (skip)
|
||||
if not parameter or not operator or not values:
|
||||
evaluated_conditions.append(None)
|
||||
evaluated_conditions.append(True)
|
||||
continue
|
||||
|
||||
# Make sure the condition values is in a list
|
||||
@@ -248,25 +251,25 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
|
||||
elif parameter_type == 'float':
|
||||
values = [float(v) for v in values]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to cast condition '%s' to type '%s'."
|
||||
% (parameter, parameter_type))
|
||||
except ValueError as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to cast condition '%s', values '%s', to type '%s'."
|
||||
% (parameter, values, parameter_type))
|
||||
return False
|
||||
|
||||
# Cast the parameter value to the correct type
|
||||
try:
|
||||
if parameter_type == 'str':
|
||||
parameter_value = unicode(parameters[parameter]).lower()
|
||||
parameter_value = unicode(parameter_value).lower()
|
||||
|
||||
elif parameter_type == 'int':
|
||||
parameter_value = int(parameters[parameter])
|
||||
parameter_value = int(parameter_value)
|
||||
|
||||
elif parameter_type == 'float':
|
||||
parameter_value = float(parameters[parameter])
|
||||
parameter_value = float(parameter_value)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to cast parameter '%s' to type '%s'."
|
||||
% (parameter, parameter_type))
|
||||
except ValueError as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to cast parameter '%s', value '%s', to type '%s'."
|
||||
% (parameter, parameter_value, parameter_type))
|
||||
return False
|
||||
|
||||
# Check each condition
|
||||
@@ -298,12 +301,15 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
|
||||
logger.warn(u"Tautulli NotificationHandler :: Invalid condition operator '%s'." % operator)
|
||||
evaluated_conditions.append(None)
|
||||
|
||||
if logic_groups:
|
||||
# Format and evaluate the logic string
|
||||
try:
|
||||
evaluated_logic = helpers.eval_logic_groups_to_bool(logic_groups, evaluated_conditions)
|
||||
except Exception as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to evaluate custom condition logic: %s." % e)
|
||||
return False
|
||||
else:
|
||||
evaluated_logic = all(evaluated_conditions[1:])
|
||||
|
||||
logger.debug(u"Tautulli NotificationHandler :: Custom condition evaluated to '%s'." % str(evaluated_logic))
|
||||
return evaluated_logic
|
||||
@@ -326,7 +332,7 @@ def notify(notifier_id=None, notify_action=None, stream_data=None, timeline_data
|
||||
if not notifier_config:
|
||||
return
|
||||
|
||||
if notify_action == 'test':
|
||||
if notify_action in ('test', 'api'):
|
||||
subject = kwargs.pop('subject', 'Tautulli')
|
||||
body = kwargs.pop('body', 'Test Notification')
|
||||
script_args = kwargs.pop('script_args', [])
|
||||
@@ -344,8 +350,8 @@ def notify(notifier_id=None, notify_action=None, stream_data=None, timeline_data
|
||||
|
||||
# Set the notification state in the db
|
||||
notification_id = set_notify_state(session=stream_data or timeline_data,
|
||||
notify_action=notify_action,
|
||||
notifier=notifier_config,
|
||||
notify_action=notify_action,
|
||||
subject=subject,
|
||||
body=body,
|
||||
script_args=script_args)
|
||||
@@ -384,9 +390,9 @@ def get_notify_state(session):
|
||||
return notify_states
|
||||
|
||||
|
||||
def set_notify_state(notify_action, notifier, subject, body, script_args, session=None):
|
||||
def set_notify_state(notifier, notify_action, subject='', body='', script_args='', session=None):
|
||||
|
||||
if notify_action and notifier:
|
||||
if notifier and notify_action:
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
session = session or {}
|
||||
@@ -451,7 +457,11 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
||||
|
||||
notify_params = defaultdict(str)
|
||||
if session:
|
||||
# Reload json from raw stream info
|
||||
if session.get('raw_stream_info'):
|
||||
session.update(json.loads(session['raw_stream_info']))
|
||||
notify_params.update(session)
|
||||
|
||||
if timeline:
|
||||
notify_params.update(timeline)
|
||||
|
||||
@@ -513,7 +523,7 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
||||
remaining_duration = duration - view_offset
|
||||
|
||||
# Build Plex URL
|
||||
notify_params['plex_url'] = '{web_url}#!/server/{pms_identifier}/details?key=%2Flibrary%2Fnotify_params%2F{rating_key}'.format(
|
||||
notify_params['plex_url'] = '{web_url}#!/server/{pms_identifier}/details?key=%2Flibrary%2Fmetadata%2F{rating_key}'.format(
|
||||
web_url=plexpy.CONFIG.PMS_WEB_URL,
|
||||
pms_identifier=plexpy.CONFIG.PMS_IDENTIFIER,
|
||||
rating_key=rating_key)
|
||||
@@ -937,7 +947,7 @@ def build_notify_text(subject='', body='', notify_action=None, parameters=None,
|
||||
try:
|
||||
script_args = [custom_formatter.format(unicode(arg), **parameters) for arg in subject.split()]
|
||||
except LookupError as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to parse field %s in script argument. Using fallback." % e)
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in script argument. Using fallback." % e)
|
||||
script_args = []
|
||||
except Exception as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom script arguments: %s. Using fallback." % e)
|
||||
@@ -948,7 +958,7 @@ def build_notify_text(subject='', body='', notify_action=None, parameters=None,
|
||||
try:
|
||||
subject = custom_formatter.format(unicode(subject), **parameters)
|
||||
except LookupError as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in notification subject. Using fallback." % e)
|
||||
subject = unicode(default_subject).format(**parameters)
|
||||
except Exception as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom notification subject: %s. Using fallback." % e)
|
||||
@@ -957,7 +967,7 @@ def build_notify_text(subject='', body='', notify_action=None, parameters=None,
|
||||
try:
|
||||
body = custom_formatter.format(unicode(body), **parameters)
|
||||
except LookupError as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in notification body. Using fallback." % e)
|
||||
body = unicode(default_body).format(**parameters)
|
||||
except Exception as e:
|
||||
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom notification body: %s. Using fallback." % e)
|
||||
|
@@ -61,7 +61,6 @@ import mobile_app
|
||||
import pmsconnect
|
||||
import request
|
||||
from plexpy.config import _BLACKLIST_KEYS, _WHITELIST_KEYS
|
||||
from plexpy.helpers import checked
|
||||
|
||||
|
||||
AGENT_IDS = {'growl': 0,
|
||||
|
@@ -542,8 +542,8 @@ class PmsConnect(object):
|
||||
|
||||
if metadata:
|
||||
_cache_time = metadata.pop('_cache_time', 0)
|
||||
# Return cached metadata if less than 30 minutes ago
|
||||
if int(time.time()) - _cache_time <= 1800:
|
||||
# Return cached metadata if less than METADATA_CACHE_SECONDS ago
|
||||
if int(time.time()) - _cache_time <= plexpy.CONFIG.METADATA_CACHE_SECONDS:
|
||||
return metadata
|
||||
|
||||
if rating_key:
|
||||
@@ -1155,9 +1155,9 @@ class PmsConnect(object):
|
||||
metadata['media_info'] = medias
|
||||
|
||||
if metadata:
|
||||
if cache_key:
|
||||
metadata['_cache_time'] = int(time.time())
|
||||
|
||||
if cache_key:
|
||||
out_file_path = os.path.join(plexpy.CONFIG.CACHE_DIR, 'metadata-sessionKey-%s.json' % cache_key)
|
||||
try:
|
||||
with open(out_file_path, 'w') as outFile:
|
||||
|
@@ -1,2 +1,2 @@
|
||||
PLEXPY_BRANCH = "beta"
|
||||
PLEXPY_RELEASE_VERSION = "v2.0.10-beta"
|
||||
PLEXPY_RELEASE_VERSION = "v2.0.12-beta"
|
||||
|
@@ -96,7 +96,8 @@ def check_credentials(username, password, admin_login='0'):
|
||||
if plexpy.CONFIG.HTTP_HASHED_PASSWORD and \
|
||||
username == plexpy.CONFIG.HTTP_USERNAME and check_hash(password, plexpy.CONFIG.HTTP_PASSWORD):
|
||||
return True, u'admin'
|
||||
elif username == plexpy.CONFIG.HTTP_USERNAME and password == plexpy.CONFIG.HTTP_PASSWORD:
|
||||
elif not plexpy.CONFIG.HTTP_HASHED_PASSWORD and \
|
||||
username == plexpy.CONFIG.HTTP_USERNAME and password == plexpy.CONFIG.HTTP_PASSWORD:
|
||||
return True, u'admin'
|
||||
elif not admin_login == '1' and plexpy.CONFIG.ALLOW_GUEST_ACCESS and user_login(username, password):
|
||||
return True, u'guest'
|
||||
|
@@ -3079,7 +3079,6 @@ class WebInterface(object):
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi("notify")
|
||||
def send_notification(self, notifier_id=None, subject='Tautulli', body='Test notification', notify_action='', **kwargs):
|
||||
""" Send a notification using Tautulli.
|
||||
|
||||
@@ -4440,7 +4439,9 @@ class WebInterface(object):
|
||||
counts = {'stream_count_direct_play': 0,
|
||||
'stream_count_direct_stream': 0,
|
||||
'stream_count_transcode': 0,
|
||||
'total_bandwidth': 0}
|
||||
'total_bandwidth': 0,
|
||||
'lan_bandwidth': 0,
|
||||
'wan_bandwidth': 0}
|
||||
|
||||
for s in result['sessions']:
|
||||
if s['transcode_decision'] == 'transcode':
|
||||
@@ -4451,6 +4452,10 @@ class WebInterface(object):
|
||||
counts['stream_count_direct_play'] += 1
|
||||
|
||||
counts['total_bandwidth'] += helpers.cast_to_int(s['bandwidth'])
|
||||
if s['location'] == 'lan':
|
||||
counts['lan_bandwidth'] += helpers.cast_to_int(s['bandwidth'])
|
||||
elif s['location'] == 'wan':
|
||||
counts['wan_bandwidth'] += helpers.cast_to_int(s['bandwidth'])
|
||||
|
||||
result.update(counts)
|
||||
|
||||
|
Reference in New Issue
Block a user