mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-08-14 02:26:58 -07:00
Merge pull request #282 from JonnyWong16/miscellaneous-fixes
IPv6 support, fix recently added, get server name, notify server down
This commit is contained in:
commit
9e5334ac81
20 changed files with 576 additions and 150 deletions
|
@ -7,7 +7,7 @@ from plexpy import version
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>PlexPy - ${title}</title>
|
||||
<title>PlexPy - ${title} | ${server_name}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
|
|
@ -341,6 +341,24 @@ input[type="color"],
|
|||
border-radius: 3px;
|
||||
transition: background-color .3s;
|
||||
}
|
||||
textarea.form-control {
|
||||
height: initial;
|
||||
margin: 5px 0 5px 0;
|
||||
color: #fff;
|
||||
border: 0px solid #444;
|
||||
background: #555;
|
||||
padding: 6px 12px;
|
||||
background-color: #555;
|
||||
border-radius: 3px;
|
||||
transition: background-color .3s;
|
||||
resize: none;
|
||||
}
|
||||
textarea.form-control:focus {
|
||||
outline: 0;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
transition: background-color .3s;
|
||||
}
|
||||
.pagination > li > a,
|
||||
.pagination > li > span {
|
||||
position: relative;
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="padded-header" id="library-statistics-header">
|
||||
<h3>Library Statistics</h3>
|
||||
<h3>Library Statistics <small>${config['pms_name']}</small></h3>
|
||||
</div>
|
||||
<div id="library-stats" class="library-platforms">
|
||||
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading stats...</div>
|
||||
|
@ -98,27 +98,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
function getLibraryStatsHeader() {
|
||||
$.ajax({
|
||||
"url": "get_servers_info",
|
||||
type: "post",
|
||||
cache: false,
|
||||
async: true,
|
||||
data: { },
|
||||
complete: function (xhr, status) {
|
||||
server_info = $.parseJSON(xhr.responseText);
|
||||
var server_name = 'Server name not found';
|
||||
for (var i in server_info) {
|
||||
if (server_info[i].machine_identifier == '${config['pms_identifier']}') {
|
||||
server_name = server_info[i].name
|
||||
break;
|
||||
}
|
||||
}
|
||||
$('#library-statistics-header h3').append(' <small>' + server_name + '</small>')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getLibraryStats() {
|
||||
$.ajax({
|
||||
url: 'library_stats',
|
||||
|
@ -170,7 +149,6 @@
|
|||
});
|
||||
|
||||
getHomeStats();
|
||||
getLibraryStatsHeader();
|
||||
getLibraryStats();
|
||||
|
||||
|
||||
|
|
|
@ -232,7 +232,9 @@ function getPlatformImagePath(platformName) {
|
|||
|
||||
function isPrivateIP(ip_address) {
|
||||
if (ip_address.indexOf(".") > -1) {
|
||||
var parts = ip_address.split('.');
|
||||
// get IPv4 mapped address (xxx.xxx.xxx.xxx) from IPv6 addresss (::ffff:xxx.xxx.xxx.xxx)
|
||||
var parts = ip_address.split(":");
|
||||
var parts = parts[parts.length - 1].split('.');
|
||||
if (parts[0] === '10' ||
|
||||
(parts[0] === '172' && (parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31)) ||
|
||||
(parts[0] === '192' && parts[1] === '168')) {
|
||||
|
|
|
@ -64,6 +64,20 @@ from plexpy import helpers
|
|||
</label>
|
||||
<p class="help-block">Trigger notification when a media item is added to the Plex Media Server.</p>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" data-size="small" data-id="${data['id']}" data-config-name="${data['config_prefix']}_on_extdown" ${helpers.checked(data['on_extdown'])} class="toggle-switches">
|
||||
Notify on Plex remote access down
|
||||
</label>
|
||||
<p class="help-block">Trigger notification when the Plex Media Server cannot be reached externally.</p>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" data-size="small" data-id="${data['id']}" data-config-name="${data['config_prefix']}_on_intdown" ${helpers.checked(data['on_intdown'])} class="toggle-switches">
|
||||
Notify on Plex server down
|
||||
</label>
|
||||
<p class="help-block">Trigger notification when the Plex Media Server cannot be reached internally.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -403,6 +403,12 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</label>
|
||||
<p class="help-block">Instead of polling the server at regular intervals let the server tell us when something happens. This is currently experimental. Encrypted websocket is not currently supported.</p>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="monitor_remote_access" name="monitor_remote_access" value="1" ${config['monitor_remote_access']}> Monitor Plex Remote Access
|
||||
</label>
|
||||
<p class="help-block">Enable to have PlexPy check if remote access to the Plex Media Server goes down. (Must have remote access set up in Plex.)</p>
|
||||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>History Logging</h3>
|
||||
|
@ -483,7 +489,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Notification Tuning</h3>
|
||||
<h3>Current Activity Notifications</h3>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -502,12 +508,27 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</label>
|
||||
<p class="help-block">Disable to prevent consecutive notifications (i.e. both watched & stopped notifications).</p>
|
||||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Recently Added Notifications</h3>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="notify_recently_added_grandparent" id="notify_recently_added_grandparent" value="1" ${config['notify_recently_added_grandparent']}> Group notifications for recently added TV Shows or Music
|
||||
</label>
|
||||
<p class="help-block">Enable to only get one notification for recently added Episodes or Tracks. Movies are unaffected.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_recently_added_delay">Notification Delay</label>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control" data-parsley-type="integer" id="notify_recently_added_delay" name="notify_recently_added_delay" value="${config['notify_recently_added_delay']}" size="5" data-parsley-min="60" data-parsley-trigger="change" data-parsley-errors-container="#notify_recently_added_delay_error" required>
|
||||
</div>
|
||||
<div id="notify_recently_added_delay_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Set the delay for recently added notifications to allow metadata to be processes. Minimum 60 seconds.</p>
|
||||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Custom Notification Messages</h3>
|
||||
|
@ -523,7 +544,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
<br/>
|
||||
<ul id="accordion-session" class="accordion list-unstyled">
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-play"></i>Playback Start<i class="fa fa-chevron-down"></i></div>
|
||||
<div class="link"><i class="fa fa-play fa-fw"></i> Playback Start<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
|
@ -533,14 +554,14 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_start_body_text">Message Body</label>
|
||||
<input class="form-control" type="text" id="notify_on_start_body_text" name="notify_on_start_body_text" value="${config['notify_on_start_body_text']}" data-parsley-trigger="change" required>
|
||||
<textarea class="form-control" id="notify_on_start_body_text" name="notify_on_start_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_start_body_text']}</textarea>
|
||||
<p class="help-block">Set a custom body.</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-stop"></i>Playback Stop<i class="fa fa-chevron-down"></i></div>
|
||||
<div class="link"><i class="fa fa-stop fa-fw"></i> Playback Stop<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
|
@ -550,14 +571,14 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_stop_body_text">Message Body</label>
|
||||
<input class="form-control" type="text" id="notify_on_stop_body_text" name="notify_on_stop_body_text" value="${config['notify_on_stop_body_text']}" data-parsley-trigger="change" required>
|
||||
<textarea class="form-control" id="notify_on_stop_body_text" name="notify_on_stop_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_stop_body_text']}</textarea>
|
||||
<p class="help-block">Set a custom body.</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-pause"></i>Playback Pause<i class="fa fa-chevron-down"></i></div>
|
||||
<div class="link"><i class="fa fa-pause fa-fw"></i> Playback Pause<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
|
@ -567,14 +588,14 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_pause_body_text">Message Body</label>
|
||||
<input class="form-control" type="text" id="notify_on_pause_body_text" name="notify_on_pause_body_text" value="${config['notify_on_pause_body_text']}" data-parsley-trigger="change" required>
|
||||
<textarea class="form-control" id="notify_on_pause_body_text" name="notify_on_pause_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_pause_body_text']}</textarea>
|
||||
<p class="help-block">Set a custom body.</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-play"></i>Playback Resume<i class="fa fa-chevron-down"></i></div>
|
||||
<div class="link"><i class="fa fa-play fa-fw"></i> Playback Resume<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
|
@ -584,14 +605,14 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_resume_body_text">Message Body</label>
|
||||
<input class="form-control" type="text" id="notify_on_resume_body_text" name="notify_on_resume_body_text" value="${config['notify_on_resume_body_text']}" data-parsley-trigger="change" required>
|
||||
<textarea class="form-control" id="notify_on_resume_body_text" name="notify_on_resume_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_resume_body_text']}</textarea>
|
||||
<p class="help-block">Set a custom body.</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-eye"></i>Watched<i class="fa fa-chevron-down"></i></div>
|
||||
<div class="link"><i class="fa fa-eye fa-fw"></i> Watched<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
|
@ -601,14 +622,14 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_watched_body_text">Message Body</label>
|
||||
<input class="form-control" type="text" id="notify_on_watched_body_text" name="notify_on_watched_body_text" value="${config['notify_on_watched_body_text']}" data-parsley-trigger="change" required>
|
||||
<textarea class="form-control" id="notify_on_watched_body_text" name="notify_on_watched_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_watched_body_text']}</textarea>
|
||||
<p class="help-block">Set a custom body.</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-spinner"></i>Buffer Warnings<i class="fa fa-chevron-down"></i></div>
|
||||
<div class="link"><i class="fa fa-spinner fa-fw"></i> Buffer Warnings<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
|
@ -618,7 +639,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_buffer_body_text">Message Body</label>
|
||||
<input class="form-control" type="text" id="notify_on_buffer_body_text" name="notify_on_buffer_body_text" value="${config['notify_on_buffer_body_text']}" data-parsley-trigger="change" required>
|
||||
<textarea class="form-control" id="notify_on_buffer_body_text" name="notify_on_buffer_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_buffer_body_text']}</textarea>
|
||||
<p class="help-block">Set a custom body.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -627,7 +648,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</ul>
|
||||
<ul id="accordion-timeline" class="accordion list-unstyled">
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-download"></i>Recently Added<i class="fa fa-chevron-down"></i></div>
|
||||
<div class="link"><i class="fa fa-download fa-fw"></i> Recently Added<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
|
@ -637,7 +658,41 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_created_body_text">Message Body</label>
|
||||
<input class="form-control" type="text" id="notify_on_created_body_text" name="notify_on_created_body_text" value="${config['notify_on_created_body_text']}" data-parsley-trigger="change" required>
|
||||
<textarea class="form-control" id="notify_on_created_body_text" name="notify_on_created_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_created_body_text']}</textarea>
|
||||
<p class="help-block">Set a custom body.</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-server fa-fw"></i> Plex Remote Access Down<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_extdown_subject_text">Subject Line</label>
|
||||
<input class="form-control" type="text" id="notify_on_extdown_subject_text" name="notify_on_extdown_subject_text" value="${config['notify_on_extdown_subject_text']}" data-parsley-trigger="change" required>
|
||||
<p class="help-block">Set a custom subject line.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_extdown_body_text">Message Body</label>
|
||||
<textarea class="form-control" id="notify_on_extdown_body_text" name="notify_on_extdown_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_extdown_body_text']}</textarea>
|
||||
<p class="help-block">Set a custom body.</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-server fa-fw"></i> Plex Server Down<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_intdown_subject_text">Subject Line</label>
|
||||
<input class="form-control" type="text" id="notify_on_intdown_subject_text" name="notify_on_intdown_subject_text" value="${config['notify_on_intdown_subject_text']}" data-parsley-trigger="change" required>
|
||||
<p class="help-block">Set a custom subject line.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_intdown_body_text">Message Body</label>
|
||||
<textarea class="form-control" id="notify_on_intdown_body_text" name="notify_on_intdown_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_intdown_body_text']}</textarea>
|
||||
<p class="help-block">Set a custom body.</p>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -660,7 +715,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
% for agent in available_notification_agents:
|
||||
<li>
|
||||
<span>
|
||||
% if agent['on_play'] or agent['on_stop'] or agent['on_pause'] or agent['on_resume'] or agent['on_buffer'] or agent['on_watched'] or agent['on_created']:
|
||||
% if agent['on_play'] or agent['on_stop'] or agent['on_pause'] or agent['on_resume'] or agent['on_buffer'] or agent['on_watched'] or agent['on_created'] or agent['on_extdown'] or agent['on_intdown']:
|
||||
<a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left active" data-toggle="modal"><i class="fa fa-lg fa-bell"></i></a>
|
||||
% else:
|
||||
<a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left" data-toggle="modal"><i class="fa fa-lg fa-bell"></i></a>
|
||||
|
@ -870,6 +925,10 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
|||
<td width="150"><strong>{server_name}</strong></td>
|
||||
<td>The name of your Plex Server.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="150"><strong>{server_uptime}</strong></td>
|
||||
<td>The uptime (in days, hours, mins, secs) of your Plex Server.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="150"><strong>{user}</strong></td>
|
||||
<td>The username of the person streaming.</td>
|
||||
|
@ -1252,7 +1311,7 @@ $(document).ready(function() {
|
|||
'X-Plex-Version': '${common.VERSION_NUMBER}',
|
||||
'X-Plex-Platform': '${common.PLATFORM}',
|
||||
'X-Plex-Platform-Version': '${common.PLATFORM_VERSION}',
|
||||
'X-Plex-Client-Identifier': '${config['pms_uuid']}',
|
||||
'X-Plex-Client-Identifier': '${config["pms_uuid"]}',
|
||||
'Authorization': 'Basic ' + btoa($("#pms_username").val() + ':' + $("#pms_password").val())
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
|
@ -1388,6 +1447,14 @@ $(document).ready(function() {
|
|||
e.preventDefault()
|
||||
});
|
||||
|
||||
// auto resizing textarea for custom notification message body
|
||||
$('textarea[data-autoresize]').each(function() {
|
||||
var offset = this.offsetHeight - this.clientHeight;
|
||||
var resizeTextarea = function(el) {
|
||||
$(el).css('height', 'auto').css('height', el.scrollHeight + offset);
|
||||
};
|
||||
$(this).on('focus keyup input', function() { resizeTextarea(this); }).removeAttr('data-autoresize');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
</%def>
|
|
@ -31,7 +31,7 @@ except ImportError:
|
|||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
|
||||
from plexpy import versioncheck, logger, activity_pinger, plextv
|
||||
from plexpy import versioncheck, logger, activity_pinger, plextv, pmsconnect
|
||||
import plexpy.config
|
||||
|
||||
PROG_DIR = None
|
||||
|
@ -169,6 +169,7 @@ def initialize(config_file):
|
|||
# Get the real PMS urls for SSL and remote access
|
||||
if CONFIG.PMS_TOKEN and CONFIG.PMS_IP and CONFIG.PMS_PORT:
|
||||
plextv.get_real_pms_url()
|
||||
pmsconnect.get_server_friendly_name()
|
||||
|
||||
# Refresh the users list on startup
|
||||
if CONFIG.PMS_TOKEN and CONFIG.REFRESH_USERS_ON_STARTUP:
|
||||
|
@ -280,14 +281,19 @@ def initialize_scheduler():
|
|||
seconds = 0
|
||||
|
||||
if CONFIG.PMS_IP and CONFIG.PMS_TOKEN:
|
||||
schedule_job(plextv.get_real_pms_url, 'Refresh Plex Server URLs', hours=12, minutes=0, seconds=0)
|
||||
schedule_job(plextv.get_real_pms_url, 'Refresh Plex Server URLs',
|
||||
hours=12, minutes=0, seconds=0)
|
||||
schedule_job(pmsconnect.get_server_friendly_name, 'Refresh Plex Server Name',
|
||||
hours=12, minutes=0, seconds=0)
|
||||
schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
|
||||
hours=0, minutes=0, seconds=seconds)
|
||||
schedule_job(activity_pinger.check_server_response, 'Check for server response',
|
||||
hours=0, minutes=0, seconds=seconds)
|
||||
|
||||
# If we're not using websockets then fall back to polling
|
||||
if not CONFIG.MONITORING_USE_WEBSOCKET or POLLING_FAILOVER:
|
||||
schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions',
|
||||
hours=0, minutes=0, seconds=seconds)
|
||||
schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
|
||||
hours=0, minutes=0, seconds=seconds)
|
||||
|
||||
# Refresh the users list
|
||||
if CONFIG.REFRESH_USERS_INTERVAL:
|
||||
|
|
|
@ -218,7 +218,7 @@ class TimelineHandler(object):
|
|||
|
||||
def __init__(self, timeline):
|
||||
self.timeline = timeline
|
||||
logger.debug(timeline)
|
||||
#logger.debug(timeline)
|
||||
|
||||
def is_item(self):
|
||||
if 'itemID' in self.timeline:
|
||||
|
|
|
@ -13,13 +13,16 @@
|
|||
# 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, database, helpers, activity_processor
|
||||
from plexpy import logger, pmsconnect, plextv, notification_handler, database, helpers, activity_processor
|
||||
|
||||
import threading
|
||||
import plexpy
|
||||
import time
|
||||
import urllib2
|
||||
|
||||
monitor_lock = threading.Lock()
|
||||
ext_ping_count = 0
|
||||
int_ping_count = 0
|
||||
|
||||
|
||||
def check_active_sessions(ws_request=False):
|
||||
|
@ -162,12 +165,14 @@ def check_active_sessions(ws_request=False):
|
|||
monitor_process.write_session(session)
|
||||
else:
|
||||
logger.debug(u"PlexPy Monitor :: Unable to read session list.")
|
||||
|
||||
|
||||
def check_recently_added():
|
||||
|
||||
with monitor_lock:
|
||||
current_time = int(time.time())
|
||||
# add delay to allow for metadata processing
|
||||
delay = plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY
|
||||
time_threshold = int(time.time()) - delay
|
||||
time_interval = plexpy.CONFIG.MONITORING_INTERVAL
|
||||
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
recently_added_list = pms_connect.get_recently_added_details(count='10')
|
||||
|
@ -176,36 +181,91 @@ def check_recently_added():
|
|||
recently_added = recently_added_list['recently_added']
|
||||
|
||||
for item in recently_added:
|
||||
if int(item['added_at']) >= current_time - plexpy.CONFIG.MONITORING_INTERVAL:
|
||||
if item['media_type'] == 'movie':
|
||||
metadata_list = pms_connect.get_metadata_details(item['rating_key'])
|
||||
if metadata_list:
|
||||
metadata = [metadata_list['metadata']]
|
||||
else:
|
||||
logger.error(u"PlexPy Monitor :: Unable to retrieve metadata for rating_key %s" % str(item['rating_key']))
|
||||
|
||||
elif plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT:
|
||||
metadata_list = pms_connect.get_metadata_details(item['parent_rating_key'])
|
||||
if metadata_list:
|
||||
metadata = [metadata_list['metadata']]
|
||||
else:
|
||||
logger.error(u"PlexPy Monitor :: Unable to retrieve metadata for parent_rating_key %s" % str(item['parent_rating_key']))
|
||||
|
||||
if item['media_type'] == 'movie':
|
||||
metadata_list = pms_connect.get_metadata_details(item['rating_key'])
|
||||
if metadata_list:
|
||||
metadata = [metadata_list['metadata']]
|
||||
else:
|
||||
metadata_list = pms_connect.get_metadata_children_details(item['rating_key'])
|
||||
if metadata_list:
|
||||
metadata = metadata_list['metadata']
|
||||
else:
|
||||
logger.error(u"PlexPy Monitor :: Unable to retrieve children metadata for rating_key" % str(item['rating_key']))
|
||||
logger.error(u"PlexPy Monitor :: Unable to retrieve metadata for rating_key %s" \
|
||||
% str(item['rating_key']))
|
||||
|
||||
if metadata:
|
||||
else:
|
||||
metadata_list = pms_connect.get_metadata_children_details(item['rating_key'])
|
||||
if metadata_list:
|
||||
metadata = metadata_list['metadata']
|
||||
else:
|
||||
logger.error(u"PlexPy Monitor :: Unable to retrieve children metadata for rating_key %s" \
|
||||
% str(item['rating_key']))
|
||||
|
||||
if metadata:
|
||||
if not plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT:
|
||||
for item in metadata:
|
||||
if (plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT \
|
||||
and int(item['updated_at']) >= current_time - plexpy.CONFIG.MONITORING_INTERVAL) \
|
||||
or (not plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT \
|
||||
and int(item['added_at']) >= current_time - plexpy.CONFIG.MONITORING_INTERVAL):
|
||||
logger.debug(u"PlexPy Monitor :: Library item %s has been added to Plex." % str(item['rating_key']))
|
||||
|
||||
if 0 < int(item['added_at']) - time_threshold <= time_interval:
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(timeline_data=item, notify_action='created')).start()
|
||||
|
||||
else:
|
||||
item = max(metadata, key=lambda x:x['added_at'])
|
||||
|
||||
if 0 < int(item['added_at']) - time_threshold <= time_interval:
|
||||
if item['media_type'] == 'episode' or item['media_type'] == 'track':
|
||||
metadata_list = pms_connect.get_metadata_details(item['grandparent_rating_key'])
|
||||
|
||||
if metadata_list:
|
||||
item = metadata_list['metadata']
|
||||
else:
|
||||
logger.error(u"PlexPy Monitor :: Unable to retrieve grandparent metadata for grandparent_rating_key %s" \
|
||||
% str(item['rating_key']))
|
||||
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(timeline_data=item, notify_action='created')).start()
|
||||
|
||||
def check_server_response():
|
||||
|
||||
with monitor_lock:
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
internal_response = pms_connect.get_server_response()
|
||||
global int_ping_count
|
||||
|
||||
if not internal_response:
|
||||
int_ping_count += 1
|
||||
logger.warn(u"PlexPy Monitor :: Unable to get an internal response from the server, ping attempt %s." \
|
||||
% str(int_ping_count))
|
||||
else:
|
||||
int_ping_count = 0
|
||||
|
||||
if plexpy.CONFIG.MONITOR_REMOTE_ACCESS:
|
||||
plex_tv = plextv.PlexTV()
|
||||
external_response = plex_tv.get_server_response()
|
||||
global ext_ping_count
|
||||
|
||||
if not external_response:
|
||||
ext_ping_count += 1
|
||||
logger.warn(u"PlexPy Monitor :: Plex remote access port mapping failed, ping attempt %s." \
|
||||
% str(ext_ping_count))
|
||||
else:
|
||||
host = external_response[0]['host']
|
||||
port = external_response[0]['port']
|
||||
|
||||
try:
|
||||
http_response = urllib2.urlopen('http://' + host + ':' + port)
|
||||
except urllib2.HTTPError, e:
|
||||
ext_ping_count = 0
|
||||
except urllib2.URLError, e:
|
||||
ext_ping_count += 1
|
||||
logger.warn(u"PlexPy Monitor :: Unable to get an external response from the server, ping attempt %s." \
|
||||
% str(ext_ping_count))
|
||||
else:
|
||||
ext_ping_count = 0
|
||||
|
||||
if int_ping_count == 3:
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(notify_action='intdown')).start()
|
||||
|
||||
if ext_ping_count == 3:
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(notify_action='extdown')).start()
|
|
@ -279,10 +279,17 @@ class ActivityProcessor(object):
|
|||
if ipv4:
|
||||
# The logged IP will always be the first match and we don't want localhost entries
|
||||
if ipv4[0] != '127.0.0.1':
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s "
|
||||
u"and machineIdentifier %s."
|
||||
% (ipv4[0], rating_key, machine_id))
|
||||
return ipv4[0]
|
||||
# check if IPv4 mapped IPv6 address (::ffff:xxx.xxx.xxx.xxx)
|
||||
if '::ffff:' + ipv4[0] in line:
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s "
|
||||
u"and machineIdentifier %s."
|
||||
% ('::ffff:' + ipv4[0], rating_key, machine_id))
|
||||
return '::ffff:' + ipv4[0]
|
||||
else:
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s "
|
||||
u"and machineIdentifier %s."
|
||||
% (ipv4[0], rating_key, machine_id))
|
||||
return ipv4[0]
|
||||
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Unable to find IP address on first pass. "
|
||||
u"Attempting fallback check in 5 seconds...")
|
||||
|
@ -302,9 +309,14 @@ class ActivityProcessor(object):
|
|||
if ipv4:
|
||||
# The logged IP will always be the first match and we don't want localhost entries
|
||||
if ipv4[0] != '127.0.0.1':
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s." %
|
||||
(ipv4[0], rating_key))
|
||||
return ipv4[0]
|
||||
if '::ffff:' + ipv4[0] in line:
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s." %
|
||||
('::ffff:' + ipv4[0], rating_key))
|
||||
return '::ffff:' + ipv4[0]
|
||||
else:
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s." %
|
||||
(ipv4[0], rating_key))
|
||||
return ipv4[0]
|
||||
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Unable to find IP address on fallback search. Not logging IP address.")
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ _CONFIG_DEFINITIONS = {
|
|||
'PMS_IP': (str, 'PMS', '127.0.0.1'),
|
||||
'PMS_IS_REMOTE': (int, 'PMS', 0),
|
||||
'PMS_LOGS_FOLDER': (str, 'PMS', ''),
|
||||
'PMS_NAME': (str, 'PMS', ''),
|
||||
'PMS_PORT': (int, 'PMS', 32400),
|
||||
'PMS_TOKEN': (str, 'PMS', ''),
|
||||
'PMS_SSL': (int, 'General', 0),
|
||||
|
@ -45,6 +46,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'BOXCAR_ON_BUFFER': (int, 'Boxcar', 0),
|
||||
'BOXCAR_ON_WATCHED': (int, 'Boxcar', 0),
|
||||
'BOXCAR_ON_CREATED': (int, 'Boxcar', 0),
|
||||
'BOXCAR_ON_EXTDOWN': (int, 'Boxcar', 0),
|
||||
'BOXCAR_ON_INTDOWN': (int, 'Boxcar', 0),
|
||||
'BUFFER_THRESHOLD': (int, 'Monitoring', 3),
|
||||
'BUFFER_WAIT': (int, 'Monitoring', 900),
|
||||
'CACHE_DIR': (str, 'General', ''),
|
||||
|
@ -70,6 +73,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'EMAIL_ON_BUFFER': (int, 'Email', 0),
|
||||
'EMAIL_ON_WATCHED': (int, 'Email', 0),
|
||||
'EMAIL_ON_CREATED': (int, 'Email', 0),
|
||||
'EMAIL_ON_EXTDOWN': (int, 'Email', 0),
|
||||
'EMAIL_ON_INTDOWN': (int, 'Email', 0),
|
||||
'ENABLE_HTTPS': (int, 'General', 0),
|
||||
'FIRST_RUN_COMPLETE': (int, 'General', 0),
|
||||
'FREEZE_DB': (int, 'General', 0),
|
||||
|
@ -87,6 +92,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'GROWL_ON_BUFFER': (int, 'Growl', 0),
|
||||
'GROWL_ON_WATCHED': (int, 'Growl', 0),
|
||||
'GROWL_ON_CREATED': (int, 'Growl', 0),
|
||||
'GROWL_ON_EXTDOWN': (int, 'Growl', 0),
|
||||
'GROWL_ON_INTDOWN': (int, 'Growl', 0),
|
||||
'HOME_LIBRARY_CARDS': (str, 'General', 'library_statistics_first'),
|
||||
'HOME_STATS_LENGTH': (int, 'General', 30),
|
||||
'HOME_STATS_TYPE': (int, 'General', 0),
|
||||
|
@ -112,6 +119,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'IFTTT_ON_BUFFER': (int, 'IFTTT', 0),
|
||||
'IFTTT_ON_WATCHED': (int, 'IFTTT', 0),
|
||||
'IFTTT_ON_CREATED': (int, 'IFTTT', 0),
|
||||
'IFTTT_ON_EXTDOWN': (int, 'IFTTT', 0),
|
||||
'IFTTT_ON_INTDOWN': (int, 'IFTTT', 0),
|
||||
'JOURNAL_MODE': (str, 'Advanced', 'wal'),
|
||||
'LAUNCH_BROWSER': (int, 'General', 1),
|
||||
'LOG_DIR': (str, 'General', ''),
|
||||
|
@ -125,6 +134,7 @@ _CONFIG_DEFINITIONS = {
|
|||
'MUSIC_NOTIFY_ON_STOP': (int, 'Monitoring', 0),
|
||||
'MUSIC_NOTIFY_ON_PAUSE': (int, 'Monitoring', 0),
|
||||
'MUSIC_LOGGING_ENABLE': (int, 'Monitoring', 0),
|
||||
'MONITOR_REMOTE_ACCESS': (int, 'Monitoring', 0),
|
||||
'MONITORING_INTERVAL': (int, 'Monitoring', 60),
|
||||
'MONITORING_USE_WEBSOCKET': (int, 'Monitoring', 0),
|
||||
'NMA_APIKEY': (str, 'NMA', ''),
|
||||
|
@ -137,8 +147,11 @@ _CONFIG_DEFINITIONS = {
|
|||
'NMA_ON_BUFFER': (int, 'NMA', 0),
|
||||
'NMA_ON_WATCHED': (int, 'NMA', 0),
|
||||
'NMA_ON_CREATED': (int, 'NMA', 0),
|
||||
'NMA_ON_EXTDOWN': (int, 'NMA', 0),
|
||||
'NMA_ON_INTDOWN': (int, 'NMA', 0),
|
||||
'NOTIFY_CONSECUTIVE': (int, 'Monitoring', 1),
|
||||
'NOTIFY_RECENTLY_ADDED_GRANDPARENT': (int, 'Monitoring', 0),
|
||||
'NOTIFY_RECENTLY_ADDED_DELAY': (int, 'Monitoring', 60),
|
||||
'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}.'),
|
||||
|
@ -154,6 +167,10 @@ _CONFIG_DEFINITIONS = {
|
|||
'NOTIFY_ON_WATCHED_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has watched {title}.'),
|
||||
'NOTIFY_ON_CREATED_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
|
||||
'NOTIFY_ON_CREATED_BODY_TEXT': (str, 'Monitoring', '{title} was recently added to Plex.'),
|
||||
'NOTIFY_ON_EXTDOWN_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
|
||||
'NOTIFY_ON_EXTDOWN_BODY_TEXT': (str, 'Monitoring', 'The Plex Media Server remote access is down.'),
|
||||
'NOTIFY_ON_INTDOWN_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
|
||||
'NOTIFY_ON_INTDOWN_BODY_TEXT': (str, 'Monitoring', 'The Plex Media Server is down.'),
|
||||
'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/PlexPy'),
|
||||
'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0),
|
||||
'OSX_NOTIFY_ON_PLAY': (int, 'OSX_Notify', 0),
|
||||
|
@ -163,6 +180,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'OSX_NOTIFY_ON_BUFFER': (int, 'OSX_Notify', 0),
|
||||
'OSX_NOTIFY_ON_WATCHED': (int, 'OSX_Notify', 0),
|
||||
'OSX_NOTIFY_ON_CREATED': (int, 'OSX_Notify', 0),
|
||||
'OSX_NOTIFY_ON_EXTDOWN': (int, 'OSX_Notify', 0),
|
||||
'OSX_NOTIFY_ON_INTDOWN': (int, 'OSX_Notify', 0),
|
||||
'PLEX_CLIENT_HOST': (str, 'Plex', ''),
|
||||
'PLEX_ENABLED': (int, 'Plex', 0),
|
||||
'PLEX_PASSWORD': (str, 'Plex', ''),
|
||||
|
@ -174,6 +193,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'PLEX_ON_BUFFER': (int, 'Plex', 0),
|
||||
'PLEX_ON_WATCHED': (int, 'Plex', 0),
|
||||
'PLEX_ON_CREATED': (int, 'Plex', 0),
|
||||
'PLEX_ON_EXTDOWN': (int, 'Plex', 0),
|
||||
'PLEX_ON_INTDOWN': (int, 'Plex', 0),
|
||||
'PROWL_ENABLED': (int, 'Prowl', 0),
|
||||
'PROWL_KEYS': (str, 'Prowl', ''),
|
||||
'PROWL_PRIORITY': (int, 'Prowl', 0),
|
||||
|
@ -184,6 +205,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'PROWL_ON_BUFFER': (int, 'Prowl', 0),
|
||||
'PROWL_ON_WATCHED': (int, 'Prowl', 0),
|
||||
'PROWL_ON_CREATED': (int, 'Prowl', 0),
|
||||
'PROWL_ON_EXTDOWN': (int, 'Prowl', 0),
|
||||
'PROWL_ON_INTDOWN': (int, 'Prowl', 0),
|
||||
'PUSHALOT_APIKEY': (str, 'Pushalot', ''),
|
||||
'PUSHALOT_ENABLED': (int, 'Pushalot', 0),
|
||||
'PUSHALOT_ON_PLAY': (int, 'Pushalot', 0),
|
||||
|
@ -193,6 +216,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'PUSHALOT_ON_BUFFER': (int, 'Pushalot', 0),
|
||||
'PUSHALOT_ON_WATCHED': (int, 'Pushalot', 0),
|
||||
'PUSHALOT_ON_CREATED': (int, 'Pushalot', 0),
|
||||
'PUSHALOT_ON_EXTDOWN': (int, 'Pushalot', 0),
|
||||
'PUSHALOT_ON_INTDOWN': (int, 'Pushalot', 0),
|
||||
'PUSHBULLET_APIKEY': (str, 'PushBullet', ''),
|
||||
'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''),
|
||||
'PUSHBULLET_CHANNEL_TAG': (str, 'PushBullet', ''),
|
||||
|
@ -204,6 +229,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'PUSHBULLET_ON_BUFFER': (int, 'PushBullet', 0),
|
||||
'PUSHBULLET_ON_WATCHED': (int, 'PushBullet', 0),
|
||||
'PUSHBULLET_ON_CREATED': (int, 'PushBullet', 0),
|
||||
'PUSHBULLET_ON_EXTDOWN': (int, 'PushBullet', 0),
|
||||
'PUSHBULLET_ON_INTDOWN': (int, 'PushBullet', 0),
|
||||
'PUSHOVER_APITOKEN': (str, 'Pushover', ''),
|
||||
'PUSHOVER_ENABLED': (int, 'Pushover', 0),
|
||||
'PUSHOVER_KEYS': (str, 'Pushover', ''),
|
||||
|
@ -216,6 +243,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'PUSHOVER_ON_BUFFER': (int, 'Pushover', 0),
|
||||
'PUSHOVER_ON_WATCHED': (int, 'Pushover', 0),
|
||||
'PUSHOVER_ON_CREATED': (int, 'Pushover', 0),
|
||||
'PUSHOVER_ON_EXTDOWN': (int, 'Pushover', 0),
|
||||
'PUSHOVER_ON_INTDOWN': (int, 'Pushover', 0),
|
||||
'REFRESH_USERS_INTERVAL': (int, 'Monitoring', 12),
|
||||
'REFRESH_USERS_ON_STARTUP': (int, 'Monitoring', 1),
|
||||
'TELEGRAM_BOT_TOKEN': (str, 'Telegram', ''),
|
||||
|
@ -228,6 +257,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'TELEGRAM_ON_BUFFER': (int, 'Telegram', 0),
|
||||
'TELEGRAM_ON_WATCHED': (int, 'Telegram', 0),
|
||||
'TELEGRAM_ON_CREATED': (int, 'Telegram', 0),
|
||||
'TELEGRAM_ON_EXTDOWN': (int, 'Telegram', 0),
|
||||
'TELEGRAM_ON_INTDOWN': (int, 'Telegram', 0),
|
||||
'TV_NOTIFY_ENABLE': (int, 'Monitoring', 0),
|
||||
'TV_NOTIFY_ON_START': (int, 'Monitoring', 1),
|
||||
'TV_NOTIFY_ON_STOP': (int, 'Monitoring', 0),
|
||||
|
@ -243,6 +274,8 @@ _CONFIG_DEFINITIONS = {
|
|||
'TWITTER_ON_BUFFER': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_WATCHED': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_CREATED': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_EXTDOWN': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_INTDOWN': (int, 'Twitter', 0),
|
||||
'UPDATE_DB_INTERVAL': (int, 'General', 24),
|
||||
'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1),
|
||||
'VIDEO_LOGGING_ENABLE': (int, 'Monitoring', 1),
|
||||
|
@ -256,7 +289,9 @@ _CONFIG_DEFINITIONS = {
|
|||
'XBMC_ON_RESUME': (int, 'XBMC', 0),
|
||||
'XBMC_ON_BUFFER': (int, 'XBMC', 0),
|
||||
'XBMC_ON_WATCHED': (int, 'XBMC', 0),
|
||||
'XBMC_ON_CREATED': (int, 'XBMC', 0)
|
||||
'XBMC_ON_CREATED': (int, 'XBMC', 0),
|
||||
'XBMC_ON_EXTDOWN': (int, 'XBMC', 0),
|
||||
'XBMC_ON_INTDOWN': (int, 'XBMC', 0)
|
||||
}
|
||||
# pylint:disable=R0902
|
||||
# it might be nice to refactor for fewer instance variables
|
||||
|
|
|
@ -41,7 +41,7 @@ class DataFactory(object):
|
|||
'SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS paused_counter',
|
||||
'session_history.user_id',
|
||||
'session_history.user',
|
||||
'(CASE WHEN users.friendly_name IS NULL THEN user ELSE users.friendly_name END) as friendly_name',
|
||||
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) as friendly_name',
|
||||
'platform',
|
||||
'player',
|
||||
'ip_address',
|
||||
|
@ -437,7 +437,7 @@ class DataFactory(object):
|
|||
top_users = []
|
||||
try:
|
||||
query = 'SELECT session_history.user, ' \
|
||||
'(case when users.friendly_name is null then session_history.user else ' \
|
||||
'(case when users.friendly_name is null then users.username else ' \
|
||||
'users.friendly_name end) as friendly_name,' \
|
||||
'COUNT(session_history.id) as total_plays, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
|
@ -536,7 +536,7 @@ class DataFactory(object):
|
|||
try:
|
||||
query = 'SELECT session_history_metadata.id, ' \
|
||||
'session_history.user, ' \
|
||||
'(case when users.friendly_name is null then session_history.user else ' \
|
||||
'(case when users.friendly_name is null then users.username else ' \
|
||||
'users.friendly_name end) as friendly_name,' \
|
||||
'users.user_id, ' \
|
||||
'users.custom_avatar_url as user_thumb, ' \
|
||||
|
|
|
@ -410,7 +410,7 @@ class Graphs(object):
|
|||
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT ' \
|
||||
'(case when users.friendly_name is null then session_history.user else ' \
|
||||
'(case when users.friendly_name is null then users.username else ' \
|
||||
'users.friendly_name end) as friendly_name,' \
|
||||
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
|
||||
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
|
||||
|
@ -427,7 +427,7 @@ class Graphs(object):
|
|||
result = monitor_db.select(query)
|
||||
else:
|
||||
query = 'SELECT ' \
|
||||
'(case when users.friendly_name is null then session_history.user else ' \
|
||||
'(case when users.friendly_name is null then users.username else ' \
|
||||
'users.friendly_name end) as friendly_name,' \
|
||||
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
|
||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# This file is part of PlexPy.
|
||||
# This file is part of PlexPy.
|
||||
#
|
||||
# PlexPy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -144,6 +144,31 @@ def now():
|
|||
now = datetime.datetime.now()
|
||||
return now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def human_duration(s):
|
||||
|
||||
hd = ''
|
||||
|
||||
if str(s).isdigit():
|
||||
d = int(s / 84600)
|
||||
h = int((s % 84600) / 3600)
|
||||
m = int(((s % 84600) % 3600) / 60)
|
||||
s = int(((s % 84600) % 3600) % 60)
|
||||
|
||||
hd_list = []
|
||||
if d > 0:
|
||||
hd_list.append(str(d) + ' days')
|
||||
if h > 0:
|
||||
hd_list.append(str(h) + ' hrs')
|
||||
if m > 0:
|
||||
hd_list.append(str(m) + ' mins')
|
||||
if s > 0:
|
||||
hd_list.append(str(s) + ' secs')
|
||||
|
||||
hd = ' '.join(hd_list)
|
||||
|
||||
return hd
|
||||
else:
|
||||
return hd
|
||||
|
||||
def get_age(date):
|
||||
|
||||
|
|
|
@ -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, config, notifiers, database, helpers
|
||||
from plexpy import logger, config, notifiers, database, helpers, plextv, pmsconnect
|
||||
|
||||
import plexpy
|
||||
import time
|
||||
|
@ -172,17 +172,27 @@ def notify_timeline(timeline_data=None, notify_action=None):
|
|||
if timeline_data and notify_action:
|
||||
for agent in notifiers.available_notification_agents():
|
||||
if agent['on_created'] and notify_action == 'created':
|
||||
if (plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT \
|
||||
and (timeline_data['media_type'] == 'movie' or timeline_data['media_type'] == 'show' or timeline_data['media_type'] == 'artist')) \
|
||||
or (not plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT \
|
||||
and (timeline_data['media_type'] == 'movie' or timeline_data['media_type'] == 'episode' or timeline_data['media_type'] == 'track')):
|
||||
# Build and send notification
|
||||
notify_strings = build_notify_text(timeline=timeline_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=timeline_data, state=notify_action, agent_info=agent)
|
||||
# Build and send notification
|
||||
notify_strings = build_notify_text(timeline=timeline_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=timeline_data, state=notify_action, agent_info=agent)
|
||||
elif not timeline_data and notify_action:
|
||||
for agent in notifiers.available_notification_agents():
|
||||
if agent['on_extdown'] and notify_action == 'extdown':
|
||||
# Build and send notification
|
||||
notify_strings = build_server_notify_text(state=notify_action)
|
||||
notifiers.send_notification(config_id=agent['id'],
|
||||
subject=notify_strings[0],
|
||||
body=notify_strings[1])
|
||||
if agent['on_intdown'] and notify_action == 'intdown':
|
||||
# Build and send notification
|
||||
notify_strings = build_server_notify_text(state=notify_action)
|
||||
notifiers.send_notification(config_id=agent['id'],
|
||||
subject=notify_strings[0],
|
||||
body=notify_strings[1])
|
||||
else:
|
||||
logger.debug(u"PlexPy Notifier :: Notify timeline called but incomplete data received.")
|
||||
|
||||
|
@ -265,19 +275,21 @@ def set_notify_state(session, state, agent_info):
|
|||
|
||||
|
||||
def build_notify_text(session=None, timeline=None, state=None):
|
||||
from plexpy import pmsconnect, helpers
|
||||
import re
|
||||
|
||||
# Get the server name
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
server_name = pms_connect.get_server_pref(pref='FriendlyName')
|
||||
# If friendly name is blank
|
||||
if not server_name:
|
||||
servers_info = pms_connect.get_servers_info()
|
||||
for server in servers_info:
|
||||
if server['machine_identifier'] == plexpy.CONFIG.PMS_IDENTIFIER:
|
||||
server_name = server['name']
|
||||
break
|
||||
server_name = plexpy.CONFIG.PMS_NAME
|
||||
|
||||
# Get the server uptime
|
||||
plex_tv = plextv.PlexTV()
|
||||
server_times = plex_tv.get_server_times()
|
||||
|
||||
if server_times:
|
||||
updated_at = server_times[0]['updated_at']
|
||||
server_uptime = helpers.human_duration(int(time.time() - helpers.cast_to_float(updated_at)))
|
||||
else:
|
||||
logger.error(u"PlexPy Notifier :: Unable to retrieve server uptime.")
|
||||
server_uptime = 'N/A'
|
||||
|
||||
# Get metadata feed for item
|
||||
if session:
|
||||
|
@ -285,6 +297,7 @@ def build_notify_text(session=None, timeline=None, state=None):
|
|||
elif timeline:
|
||||
rating_key = timeline['rating_key']
|
||||
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
metadata_list = pms_connect.get_metadata_details(rating_key=rating_key)
|
||||
|
||||
if metadata_list:
|
||||
|
@ -294,20 +307,22 @@ def build_notify_text(session=None, timeline=None, state=None):
|
|||
return []
|
||||
|
||||
# Check for exclusion tags
|
||||
if metadata['media_type'] == 'episode':
|
||||
# Regex pattern to remove the text in the tags we don't want
|
||||
pattern = re.compile('<movie>[^>]+.</movie>|<music>[^>]+.</music>', re.IGNORECASE)
|
||||
elif metadata['media_type'] == 'movie':
|
||||
if metadata['media_type'] == 'movie':
|
||||
# Regex pattern to remove the text in the tags we don't want
|
||||
pattern = re.compile('<tv>[^>]+.</tv>|<music>[^>]+.</music>', re.IGNORECASE)
|
||||
elif metadata['media_type'] == 'track':
|
||||
elif metadata['media_type'] == 'show' or metadata['media_type'] == 'episode':
|
||||
# Regex pattern to remove the text in the tags we don't want
|
||||
pattern = re.compile('<movie>[^>]+.</movie>|<music>[^>]+.</music>', re.IGNORECASE)
|
||||
elif metadata['media_type'] == 'artist' or metadata['media_type'] == 'track':
|
||||
# Regex pattern to remove the text in the tags we don't want
|
||||
pattern = re.compile('<tv>[^>]+.</tv>|<movie>[^>]+.</movie>', re.IGNORECASE)
|
||||
else:
|
||||
pattern = None
|
||||
|
||||
if metadata['media_type'] == 'episode' or metadata['media_type'] == 'movie' or metadata['media_type'] == 'track' \
|
||||
and pattern:
|
||||
if metadata['media_type'] == 'movie' \
|
||||
or metadata['media_type'] == 'show' or metadata['media_type'] == 'episode' \
|
||||
or metadata['media_type'] == 'artist' or metadata['media_type'] == 'track' \
|
||||
and pattern:
|
||||
# Remove the unwanted tags and strip any unmatch tags too.
|
||||
on_start_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT))
|
||||
on_start_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT))
|
||||
|
@ -389,6 +404,7 @@ def build_notify_text(session=None, timeline=None, state=None):
|
|||
progress_percent = helpers.get_percent(view_offset, duration)
|
||||
|
||||
available_params = {'server_name': server_name,
|
||||
'server_uptime': server_uptime,
|
||||
'user': user,
|
||||
'platform': platform,
|
||||
'player': player,
|
||||
|
@ -594,6 +610,78 @@ def build_notify_text(session=None, timeline=None, state=None):
|
|||
else:
|
||||
return None
|
||||
|
||||
def build_server_notify_text(state=None):
|
||||
# Get the server name
|
||||
server_name = plexpy.CONFIG.PMS_NAME
|
||||
|
||||
# Get the server uptime
|
||||
plex_tv = plextv.PlexTV()
|
||||
server_times = plex_tv.get_server_times()
|
||||
|
||||
if server_times:
|
||||
updated_at = server_times[0]['updated_at']
|
||||
server_uptime = helpers.human_duration(int(time.time() - helpers.cast_to_float(updated_at)))
|
||||
else:
|
||||
logger.error(u"PlexPy Notifier :: Unable to retrieve server uptime.")
|
||||
server_uptime = 'N/A'
|
||||
|
||||
on_extdown_subject = plexpy.CONFIG.NOTIFY_ON_EXTDOWN_SUBJECT_TEXT
|
||||
on_extdown_body = plexpy.CONFIG.NOTIFY_ON_EXTDOWN_BODY_TEXT
|
||||
on_intdown_subject = plexpy.CONFIG.NOTIFY_ON_INTDOWN_SUBJECT_TEXT
|
||||
on_intdown_body = plexpy.CONFIG.NOTIFY_ON_INTDOWN_BODY_TEXT
|
||||
|
||||
available_params = {'server_name': server_name,
|
||||
'server_uptime': server_uptime}
|
||||
|
||||
# Default text
|
||||
subject_text = 'PlexPy (%s)' % server_name
|
||||
|
||||
if state == 'extdown':
|
||||
# Default body text
|
||||
body_text = 'The Plex Media Server remote access is down.'
|
||||
|
||||
if on_extdown_subject and on_extdown_body:
|
||||
try:
|
||||
subject_text = unicode(on_extdown_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 = unicode(on_extdown_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 == 'intdown':
|
||||
# Default body text
|
||||
body_text = 'The Plex Media Server is down.'
|
||||
|
||||
if on_intdown_subject and on_intdown_body:
|
||||
try:
|
||||
subject_text = unicode(on_intdown_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 = unicode(on_intdown_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]
|
||||
else:
|
||||
return None
|
||||
|
||||
def strip_tag(data):
|
||||
import re
|
||||
|
|
|
@ -66,7 +66,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.GROWL_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.GROWL_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.GROWL_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.GROWL_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.GROWL_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.GROWL_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.GROWL_ON_INTDOWN
|
||||
},
|
||||
{'name': 'Prowl',
|
||||
'id': AGENT_IDS['Prowl'],
|
||||
|
@ -79,7 +81,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.PROWL_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.PROWL_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.PROWL_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.PROWL_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.PROWL_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.PROWL_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.PROWL_ON_INTDOWN
|
||||
},
|
||||
{'name': 'XBMC',
|
||||
'id': AGENT_IDS['XBMC'],
|
||||
|
@ -92,7 +96,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.XBMC_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.XBMC_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.XBMC_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.XBMC_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.XBMC_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.XBMC_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.XBMC_ON_INTDOWN
|
||||
},
|
||||
{'name': 'Plex',
|
||||
'id': AGENT_IDS['Plex'],
|
||||
|
@ -105,7 +111,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.PLEX_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.PLEX_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.PLEX_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.PLEX_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.PLEX_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.PLEX_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.PLEX_ON_INTDOWN
|
||||
},
|
||||
{'name': 'NotifyMyAndroid',
|
||||
'id': AGENT_IDS['NMA'],
|
||||
|
@ -118,7 +126,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.NMA_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.NMA_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.NMA_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.NMA_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.NMA_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.NMA_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.NMA_ON_INTDOWN
|
||||
},
|
||||
{'name': 'Pushalot',
|
||||
'id': AGENT_IDS['Pushalot'],
|
||||
|
@ -131,7 +141,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.PUSHALOT_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.PUSHALOT_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.PUSHALOT_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.PUSHALOT_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.PUSHALOT_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.PUSHALOT_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.PUSHALOT_ON_INTDOWN
|
||||
},
|
||||
{'name': 'Pushbullet',
|
||||
'id': AGENT_IDS['Pushbullet'],
|
||||
|
@ -144,7 +156,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.PUSHBULLET_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.PUSHBULLET_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.PUSHBULLET_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.PUSHBULLET_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.PUSHBULLET_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.PUSHBULLET_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.PUSHBULLET_ON_INTDOWN
|
||||
},
|
||||
{'name': 'Pushover',
|
||||
'id': AGENT_IDS['Pushover'],
|
||||
|
@ -157,7 +171,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.PUSHOVER_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.PUSHOVER_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.PUSHOVER_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.PUSHOVER_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.PUSHOVER_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.PUSHOVER_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.PUSHOVER_ON_INTDOWN
|
||||
},
|
||||
{'name': 'Boxcar2',
|
||||
'id': AGENT_IDS['Boxcar2'],
|
||||
|
@ -170,7 +186,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.BOXCAR_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.BOXCAR_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.BOXCAR_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.BOXCAR_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.BOXCAR_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.BOXCAR_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.BOXCAR_ON_INTDOWN
|
||||
},
|
||||
{'name': 'E-mail',
|
||||
'id': AGENT_IDS['Email'],
|
||||
|
@ -183,7 +201,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.EMAIL_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.EMAIL_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.EMAIL_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.EMAIL_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.EMAIL_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.EMAIL_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.EMAIL_ON_INTDOWN
|
||||
},
|
||||
{'name': 'Twitter',
|
||||
'id': AGENT_IDS['Twitter'],
|
||||
|
@ -196,7 +216,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.TWITTER_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.TWITTER_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.TWITTER_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.TWITTER_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.TWITTER_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.TWITTER_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.TWITTER_ON_INTDOWN
|
||||
},
|
||||
{'name': 'IFTTT',
|
||||
'id': AGENT_IDS['IFTTT'],
|
||||
|
@ -209,7 +231,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.IFTTT_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.IFTTT_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.IFTTT_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.IFTTT_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.IFTTT_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.IFTTT_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.IFTTT_ON_INTDOWN
|
||||
},
|
||||
{'name': 'Telegram',
|
||||
'id': AGENT_IDS['Telegram'],
|
||||
|
@ -222,7 +246,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.TELEGRAM_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.TELEGRAM_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.TELEGRAM_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.TELEGRAM_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.TELEGRAM_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.TELEGRAM_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.TELEGRAM_ON_INTDOWN
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -240,7 +266,9 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.OSX_NOTIFY_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.OSX_NOTIFY_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.OSX_NOTIFY_ON_WATCHED,
|
||||
'on_created': plexpy.CONFIG.OSX_NOTIFY_ON_CREATED
|
||||
'on_created': plexpy.CONFIG.OSX_NOTIFY_ON_CREATED,
|
||||
'on_extdown': plexpy.CONFIG.OSX_NOTIFY_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.OSX_NOTIFY_ON_INTDOWN
|
||||
})
|
||||
|
||||
return agents
|
||||
|
@ -1320,6 +1348,8 @@ class Email(object):
|
|||
|
||||
mailserver.sendmail(plexpy.CONFIG.EMAIL_FROM, plexpy.CONFIG.EMAIL_TO, message.as_string())
|
||||
mailserver.quit()
|
||||
|
||||
logger.info(u"Email notifications sent.")
|
||||
return True
|
||||
|
||||
except Exception, e:
|
||||
|
|
|
@ -444,3 +444,41 @@ class PlexTV(object):
|
|||
return clean_servers
|
||||
|
||||
return json.dumps(clean_servers, indent=4)
|
||||
|
||||
def get_server_times(self):
|
||||
servers = self.get_plextv_server_list(output_format='xml')
|
||||
server_times = []
|
||||
|
||||
try:
|
||||
xml_head = servers.getElementsByTagName('Server')
|
||||
except:
|
||||
logger.warn("Error parsing XML for Plex servers.")
|
||||
return []
|
||||
|
||||
for a in xml_head:
|
||||
if helpers.get_xml_attr(a, 'machineIdentifier') == plexpy.CONFIG.PMS_IDENTIFIER:
|
||||
server_times.append({"created_at": helpers.get_xml_attr(a, 'createdAt'),
|
||||
"updated_at": helpers.get_xml_attr(a, 'updatedAt')
|
||||
})
|
||||
break
|
||||
|
||||
return server_times
|
||||
|
||||
def get_server_response(self):
|
||||
response = self.get_plextv_server_list(output_format='xml')
|
||||
|
||||
server_url = []
|
||||
|
||||
try:
|
||||
xml_head = response.getElementsByTagName('Server')
|
||||
except:
|
||||
return False
|
||||
|
||||
for a in xml_head:
|
||||
if helpers.get_xml_attr(a, 'machineIdentifier') == plexpy.CONFIG.PMS_IDENTIFIER:
|
||||
server_url.append({"host": helpers.get_xml_attr(a, 'host'),
|
||||
"port": helpers.get_xml_attr(a, 'port')
|
||||
})
|
||||
break
|
||||
|
||||
return server_url
|
|
@ -19,6 +19,23 @@ from urlparse import urlparse
|
|||
import plexpy
|
||||
import urllib2
|
||||
|
||||
def get_server_friendly_name():
|
||||
logger.info("Requesting name from server...")
|
||||
server_name = PmsConnect().get_server_pref(pref='FriendlyName')
|
||||
|
||||
# If friendly name is blank
|
||||
if not server_name:
|
||||
servers_info = PmsConnect().get_servers_info()
|
||||
for server in servers_info:
|
||||
if server['machine_identifier'] == plexpy.CONFIG.PMS_IDENTIFIER:
|
||||
server_name = server['name']
|
||||
break
|
||||
|
||||
if server_name and server_name != plexpy.CONFIG.PMS_NAME:
|
||||
plexpy.CONFIG.__setattr__('PMS_NAME', server_name)
|
||||
plexpy.CONFIG.write()
|
||||
|
||||
return server_name
|
||||
|
||||
class PmsConnect(object):
|
||||
"""
|
||||
|
@ -1631,4 +1648,17 @@ class PmsConnect(object):
|
|||
'children': parents}
|
||||
}
|
||||
|
||||
return key_list
|
||||
return key_list
|
||||
|
||||
"""
|
||||
Check for a server response.
|
||||
|
||||
Output: bool
|
||||
"""
|
||||
def get_server_response(self):
|
||||
response = self.get_server_list()
|
||||
|
||||
if not response:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
|
|
@ -139,14 +139,14 @@ def process(opcode, data):
|
|||
activity = activity_handler.ActivityHandler(timeline=time_line[0])
|
||||
activity.process()
|
||||
|
||||
if type == 'timeline':
|
||||
try:
|
||||
time_line = info.get('_children')
|
||||
except:
|
||||
logger.debug(u"PlexPy WebSocket :: Timeline event found but unable to get timeline data.")
|
||||
return False
|
||||
#if type == 'timeline':
|
||||
# try:
|
||||
# time_line = info.get('_children')
|
||||
# except:
|
||||
# logger.debug(u"PlexPy WebSocket :: Timeline event found but unable to get timeline data.")
|
||||
# return False
|
||||
|
||||
activity = activity_handler.TimelineHandler(timeline=time_line[0])
|
||||
activity.process()
|
||||
# activity = activity_handler.TimelineHandler(timeline=time_line[0])
|
||||
# activity.process()
|
||||
|
||||
return True
|
||||
|
|
|
@ -46,9 +46,11 @@ def serve_template(templatename, **kwargs):
|
|||
|
||||
_hplookup = TemplateLookup(directories=[template_dir])
|
||||
|
||||
server_name = plexpy.CONFIG.PMS_NAME
|
||||
|
||||
try:
|
||||
template = _hplookup.get_template(templatename)
|
||||
return template.render(**kwargs)
|
||||
return template.render(server_name=server_name, **kwargs)
|
||||
except:
|
||||
return exceptions.html_error_template().render()
|
||||
|
||||
|
@ -71,7 +73,8 @@ class WebInterface(object):
|
|||
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
|
||||
"home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS,
|
||||
"home_library_cards": plexpy.CONFIG.HOME_LIBRARY_CARDS,
|
||||
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER
|
||||
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER,
|
||||
"pms_name": plexpy.CONFIG.PMS_NAME
|
||||
}
|
||||
return serve_template(templatename="index.html", title="Home", config=config)
|
||||
|
||||
|
@ -432,6 +435,7 @@ class WebInterface(object):
|
|||
"tv_notify_on_pause": checked(plexpy.CONFIG.TV_NOTIFY_ON_PAUSE),
|
||||
"movie_notify_on_pause": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_PAUSE),
|
||||
"music_notify_on_pause": checked(plexpy.CONFIG.MUSIC_NOTIFY_ON_PAUSE),
|
||||
"monitor_remote_access": checked(plexpy.CONFIG.MONITOR_REMOTE_ACCESS),
|
||||
"monitoring_interval": plexpy.CONFIG.MONITORING_INTERVAL,
|
||||
"monitoring_use_websocket": checked(plexpy.CONFIG.MONITORING_USE_WEBSOCKET),
|
||||
"refresh_users_interval": plexpy.CONFIG.REFRESH_USERS_INTERVAL,
|
||||
|
@ -443,6 +447,7 @@ class WebInterface(object):
|
|||
"pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE),
|
||||
"notify_consecutive": checked(plexpy.CONFIG.NOTIFY_CONSECUTIVE),
|
||||
"notify_recently_added_grandparent": checked(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT),
|
||||
"notify_recently_added_delay": plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY,
|
||||
"notify_watched_percent": plexpy.CONFIG.NOTIFY_WATCHED_PERCENT,
|
||||
"notify_on_start_subject_text": plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT,
|
||||
"notify_on_start_body_text": plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT,
|
||||
|
@ -458,6 +463,10 @@ class WebInterface(object):
|
|||
"notify_on_watched_body_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT,
|
||||
"notify_on_created_subject_text": plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT,
|
||||
"notify_on_created_body_text": plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT,
|
||||
"notify_on_extdown_subject_text": plexpy.CONFIG.NOTIFY_ON_EXTDOWN_SUBJECT_TEXT,
|
||||
"notify_on_extdown_body_text": plexpy.CONFIG.NOTIFY_ON_EXTDOWN_BODY_TEXT,
|
||||
"notify_on_intdown_subject_text": plexpy.CONFIG.NOTIFY_ON_INTDOWN_SUBJECT_TEXT,
|
||||
"notify_on_intdown_body_text": plexpy.CONFIG.NOTIFY_ON_INTDOWN_BODY_TEXT,
|
||||
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
|
||||
"home_stats_type": checked(plexpy.CONFIG.HOME_STATS_TYPE),
|
||||
"home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
|
||||
|
@ -482,7 +491,7 @@ class WebInterface(object):
|
|||
"tv_notify_on_stop", "movie_notify_on_stop", "music_notify_on_stop",
|
||||
"tv_notify_on_pause", "movie_notify_on_pause", "music_notify_on_pause", "refresh_users_on_startup",
|
||||
"ip_logging_enable", "video_logging_enable", "music_logging_enable", "pms_is_remote", "home_stats_type",
|
||||
"group_history_tables", "notify_consecutive", "notify_recently_added_grandparent"
|
||||
"group_history_tables", "notify_consecutive", "notify_recently_added_grandparent", "monitor_remote_access"
|
||||
]
|
||||
for checked_config in checked_configs:
|
||||
if checked_config not in kwargs:
|
||||
|
@ -1137,10 +1146,24 @@ class WebInterface(object):
|
|||
logger.warn('Unable to retrieve data.')
|
||||
|
||||
@cherrypy.expose
|
||||
def get_server_prefs(self, **kwargs):
|
||||
def get_server_friendly_name(self, **kwargs):
|
||||
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_server_prefs(output_format='json')
|
||||
result = pmsconnect.get_server_friendly_name()
|
||||
|
||||
if result:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return result
|
||||
else:
|
||||
logger.warn('Unable to retrieve data.')
|
||||
|
||||
@cherrypy.expose
|
||||
def get_server_prefs(self, pref=None, **kwargs):
|
||||
|
||||
if pref:
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_server_pref(pref=pref)
|
||||
else:
|
||||
result = None
|
||||
|
||||
if result:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue