@@ -2166,6 +1911,17 @@
});
}
+ function getNotifiersTable() {
+ $.ajax({
+ url: 'get_notifiers_table',
+ cache: false,
+ async: true,
+ complete: function(xhr, status) {
+ $("#plexpy-notifiers-table").html(xhr.responseText);
+ }
+ });
+ }
+
$(document).ready(function() {
// Javascript to enable link to tab
@@ -2204,6 +1960,7 @@ $(document).ready(function() {
$("#http_hashed_password").val($("#http_hash_password").is(":checked") ? 1 : 0)
getConfigurationTable();
getSchedulerTable();
+ getNotifiersTable();
loadUpdateDistros();
settingsChanged = false;
}
@@ -2267,6 +2024,7 @@ $(document).ready(function() {
getConfigurationTable();
getSchedulerTable();
+ getNotifiersTable();
$('#changelog-modal-link').on('click', function (e) {
e.preventDefault();
@@ -2452,39 +2210,6 @@ $(document).ready(function() {
});
});
- // Load notification agent config modal
- $(".toggle-notification-config-modal").click(function() {
- var agent_id = $(this).data('id');
- $.ajax({
- url: 'get_notification_agent_config',
- data: { agent_id: agent_id },
- cache: false,
- async: true,
- complete: function(xhr, status) {
- $("#notification-config-modal").html(xhr.responseText);
- }
- });
- });
-
- // Load notification triggers config modal
- $(".toggle-notification-triggers-modal").click(function() {
- var agent_id = $(this).data('id');
- $.ajax({
- url: 'get_notification_agent_triggers',
- data: { agent_id: agent_id },
- cache: false,
- async: true,
- complete: function(xhr, status) {
- $("#notification-triggers-modal").html(xhr.responseText);
- }
- });
- });
-
- $('#osxnotifyregister').click(function () {
- var osx_notify_app = $("#osx_notify_reg").val();
- $.get("/osxnotifyregister", { 'app': osx_notify_app }, function (data) { showMsg("
" + data + "
", false, true, 3000); });
- })
-
pms_version = false;
pms_logs_debug = false;
pms_logs = false;
@@ -2547,10 +2272,6 @@ $(document).ready(function() {
}
});
- var accordion_session = new Accordion($('#accordion-session'), false);
- var accordion_timeline = new Accordion($('#accordion-timeline'), false);
- var accordion_scripts = new Accordion($('#accordion-scripts'), false);
-
// Sortable home_sections
function set_home_sections() {
var home_sections = [];
@@ -2648,16 +2369,6 @@ $(document).ready(function() {
}
});
-
- // 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');
- });
-
$("#notify_recently_added_grandparent").change(function () {
var c = this.checked ? '#eb8600' : '#737373';
$('#notify_recently_added_grandparent_note').css('color', c);
@@ -2759,6 +2470,28 @@ $(document).ready(function() {
var distro = $("option:selected", this).data('distro')
$('#pms_update_distro').val(distro)
});
+
+ // Add a new notification agent
+ $('.new_notification_agent').click(function () {
+ $.ajax({
+ url: 'add_notifier_config',
+ data: { agent_id: $(this).data('id') },
+ cache: false,
+ async: true,
+ complete: function (xhr, status) {
+ result = $.parseJSON(xhr.responseText);
+ msg = result.message;
+ if (result.result == 'success') {
+ showMsg('
' + msg, false, true, 5000)
+ } else {
+ showMsg('
' + msg, false, true, 5000, true)
+ }
+ $('#add-notifier-modal').modal('hide');
+ getNotifiersTable();
+ }
+ });
+ });
+
});
%def>
diff --git a/plexpy/__init__.py b/plexpy/__init__.py
index b77e3745..c19f3616 100644
--- a/plexpy/__init__.py
+++ b/plexpy/__init__.py
@@ -462,6 +462,27 @@ def dbcheck():
'timestamp INTEGER, user_id INTEGER, user TEXT, user_group TEXT, ip_address TEXT, host TEXT, user_agent TEXT)'
)
+ # notifiers table :: This table keeps record of the notification agent settings
+ c_db.execute(
+ 'CREATE TABLE IF NOT EXISTS notifiers (id INTEGER PRIMARY KEY AUTOINCREMENT, '
+ 'agent_id INTEGER, agent_name TEXT, agent_label TEXT, friendly_name TEXT, notifier_config TEXT, '
+ 'on_play INTEGER DEFAULT 0, on_stop INTEGER DEFAULT 0, on_pause INTEGER DEFAULT 0, '
+ 'on_resume INTEGER DEFAULT 0, on_buffer INTEGER DEFAULT 0, on_watched INTEGER DEFAULT 0, '
+ 'on_created INTEGER DEFAULT 0, on_extdown INTEGER DEFAULT 0, on_intdown INTEGER DEFAULT 0, '
+ 'on_extup INTEGER DEFAULT 0, on_intup INTEGER DEFAULT 0, on_pmsupdate INTEGER DEFAULT 0, '
+ 'on_concurrent INTEGER DEFAULT 0, on_newdevice INTEGER DEFAULT 0, '
+ 'on_play_subject TEXT, on_stop_subject TEXT, on_pause_subject TEXT, '
+ 'on_resume_subject TEXT, on_buffer_subject TEXT, on_watched_subject TEXT, '
+ 'on_created_subject TEXT, on_extdown_subject TEXT, on_intdown_subject TEXT, '
+ 'on_extup_subject TEXT, on_intup_subject TEXT, on_pmsupdate_subject TEXT, '
+ 'on_concurrent_subject TEXT, on_newdevice_subject TEXT, '
+ 'on_play_body TEXT, on_stop_body TEXT, on_pause_body TEXT, '
+ 'on_resume_body TEXT, on_buffer_body TEXT, on_watched_body TEXT, '
+ 'on_created_body TEXT, on_extdown_body TEXT, on_intdown_body TEXT, '
+ 'on_extup_body TEXT, on_intup_body TEXT, on_pmsupdate_body TEXT, '
+ 'on_concurrent_body TEXT, on_newdevice_body TEXT)'
+ )
+
# Upgrade sessions table from earlier versions
try:
c_db.execute('SELECT started FROM sessions')
diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py
index 508b1cec..be03c329 100644
--- a/plexpy/notifiers.py
+++ b/plexpy/notifiers.py
@@ -46,576 +46,437 @@ import logger
import request
from plexpy.helpers import checked
-AGENT_IDS = {"Growl": 0,
- "Prowl": 1,
- "XBMC": 2,
- "Plex": 3,
- "NMA": 4,
- "Pushalot": 5,
- "Pushbullet": 6,
- "Pushover": 7,
- "OSX Notify": 8,
- "Boxcar2": 9,
- "Email": 10,
- "Twitter": 11,
- "IFTTT": 12,
- "Telegram": 13,
- "Slack": 14,
- "Scripts": 15,
- "Facebook": 16,
- "Browser": 17,
- "Join": 18,
- "Hipchat": 19}
+AGENT_IDS = {"growl": 0,
+ "prowl": 1,
+ "xbmc": 2,
+ "plex": 3,
+ "nma": 4,
+ "pushalot": 5,
+ "pushbullet": 6,
+ "pushover": 7,
+ "osx": 8,
+ "boxcar2": 9,
+ "email": 10,
+ "twitter": 11,
+ "ifttt": 12,
+ "telegram": 13,
+ "slack": 14,
+ "scripts": 15,
+ "facebook": 16,
+ "browser": 17,
+ "join": 18,
+ "hipchat": 19
+ }
def available_notification_agents():
- agents = [{'name': 'Growl',
- 'id': AGENT_IDS['Growl'],
- 'config_prefix': 'growl',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.GROWL_ENABLED),
- 'on_play': plexpy.CONFIG.GROWL_ON_PLAY,
- 'on_stop': plexpy.CONFIG.GROWL_ON_STOP,
- 'on_pause': plexpy.CONFIG.GROWL_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.GROWL_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.GROWL_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.GROWL_ON_WATCHED,
- 'on_created': plexpy.CONFIG.GROWL_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.GROWL_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.GROWL_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.GROWL_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.GROWL_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.GROWL_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.GROWL_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.GROWL_ON_NEWDEVICE
+ agents = [{'label': 'Growl',
+ 'name': 'growl',
+ 'id': AGENT_IDS['growl']
},
- {'name': 'Prowl',
- 'id': AGENT_IDS['Prowl'],
- 'config_prefix': 'prowl',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.PROWL_ENABLED),
- 'on_play': plexpy.CONFIG.PROWL_ON_PLAY,
- 'on_stop': plexpy.CONFIG.PROWL_ON_STOP,
- 'on_pause': plexpy.CONFIG.PROWL_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.PROWL_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.PROWL_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.PROWL_ON_WATCHED,
- 'on_created': plexpy.CONFIG.PROWL_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.PROWL_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.PROWL_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.PROWL_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.PROWL_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.PROWL_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.PROWL_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.PROWL_ON_NEWDEVICE
+ {'label': 'Prowl',
+ 'name': 'prowl',
+ 'id': AGENT_IDS['prowl']
},
- {'name': 'XBMC',
- 'id': AGENT_IDS['XBMC'],
- 'config_prefix': 'xbmc',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.XBMC_ENABLED),
- 'on_play': plexpy.CONFIG.XBMC_ON_PLAY,
- 'on_stop': plexpy.CONFIG.XBMC_ON_STOP,
- 'on_pause': plexpy.CONFIG.XBMC_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.XBMC_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.XBMC_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.XBMC_ON_WATCHED,
- 'on_created': plexpy.CONFIG.XBMC_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.XBMC_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.XBMC_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.XBMC_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.XBMC_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.XBMC_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.XBMC_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.XBMC_ON_NEWDEVICE
+ {'label': 'XBMC',
+ 'name': 'xbmc',
+ 'id': AGENT_IDS['xbmc']
},
- {'name': 'Plex Home Theater',
- 'id': AGENT_IDS['Plex'],
- 'config_prefix': 'plex',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.PLEX_ENABLED),
- 'on_play': plexpy.CONFIG.PLEX_ON_PLAY,
- 'on_stop': plexpy.CONFIG.PLEX_ON_STOP,
- 'on_pause': plexpy.CONFIG.PLEX_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.PLEX_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.PLEX_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.PLEX_ON_WATCHED,
- 'on_created': plexpy.CONFIG.PLEX_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.PLEX_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.PLEX_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.PLEX_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.PLEX_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.PLEX_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.PLEX_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.PLEX_ON_NEWDEVICE
+ {'label': 'Plex Home Theater',
+ 'name': 'plex',
+ 'id': AGENT_IDS['plex']
},
- {'name': 'NotifyMyAndroid',
- 'id': AGENT_IDS['NMA'],
- 'config_prefix': 'nma',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.NMA_ENABLED),
- 'on_play': plexpy.CONFIG.NMA_ON_PLAY,
- 'on_stop': plexpy.CONFIG.NMA_ON_STOP,
- 'on_pause': plexpy.CONFIG.NMA_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.NMA_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.NMA_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.NMA_ON_WATCHED,
- 'on_created': plexpy.CONFIG.NMA_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.NMA_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.NMA_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.NMA_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.NMA_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.NMA_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.NMA_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.NMA_ON_NEWDEVICE
+ {'label': 'Notify My Android',
+ 'name': 'nma',
+ 'id': AGENT_IDS['nma']
},
- {'name': 'Pushalot',
- 'id': AGENT_IDS['Pushalot'],
- 'config_prefix': 'pushalot',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.PUSHALOT_ENABLED),
- 'on_play': plexpy.CONFIG.PUSHALOT_ON_PLAY,
- 'on_stop': plexpy.CONFIG.PUSHALOT_ON_STOP,
- 'on_pause': plexpy.CONFIG.PUSHALOT_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.PUSHALOT_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.PUSHALOT_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.PUSHALOT_ON_WATCHED,
- 'on_created': plexpy.CONFIG.PUSHALOT_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.PUSHALOT_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.PUSHALOT_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.PUSHALOT_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.PUSHALOT_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.PUSHALOT_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.PUSHALOT_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.PUSHALOT_ON_NEWDEVICE
+ {'label': 'Pushalot',
+ 'name': 'pushalot',
+ 'id': AGENT_IDS['pushalot']
},
- {'name': 'Pushbullet',
- 'id': AGENT_IDS['Pushbullet'],
- 'config_prefix': 'pushbullet',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.PUSHBULLET_ENABLED),
- 'on_play': plexpy.CONFIG.PUSHBULLET_ON_PLAY,
- 'on_stop': plexpy.CONFIG.PUSHBULLET_ON_STOP,
- 'on_pause': plexpy.CONFIG.PUSHBULLET_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.PUSHBULLET_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.PUSHBULLET_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.PUSHBULLET_ON_WATCHED,
- 'on_created': plexpy.CONFIG.PUSHBULLET_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.PUSHBULLET_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.PUSHBULLET_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.PUSHBULLET_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.PUSHBULLET_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.PUSHBULLET_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.PUSHBULLET_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.PUSHBULLET_ON_NEWDEVICE
+ {'label': 'Pushbullet',
+ 'name': 'pushbullet',
+ 'id': AGENT_IDS['pushbullet']
},
- {'name': 'Pushover',
- 'id': AGENT_IDS['Pushover'],
- 'config_prefix': 'pushover',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.PUSHOVER_ENABLED),
- 'on_play': plexpy.CONFIG.PUSHOVER_ON_PLAY,
- 'on_stop': plexpy.CONFIG.PUSHOVER_ON_STOP,
- 'on_pause': plexpy.CONFIG.PUSHOVER_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.PUSHOVER_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.PUSHOVER_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.PUSHOVER_ON_WATCHED,
- 'on_created': plexpy.CONFIG.PUSHOVER_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.PUSHOVER_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.PUSHOVER_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.PUSHOVER_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.PUSHOVER_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.PUSHOVER_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.PUSHOVER_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.PUSHOVER_ON_NEWDEVICE
+ {'label': 'Pushover',
+ 'name': 'pushover',
+ 'id': AGENT_IDS['pushover']
},
- {'name': 'Boxcar2',
- 'id': AGENT_IDS['Boxcar2'],
- 'config_prefix': 'boxcar',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.BOXCAR_ENABLED),
- 'on_play': plexpy.CONFIG.BOXCAR_ON_PLAY,
- 'on_stop': plexpy.CONFIG.BOXCAR_ON_STOP,
- 'on_pause': plexpy.CONFIG.BOXCAR_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.BOXCAR_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.BOXCAR_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.BOXCAR_ON_WATCHED,
- 'on_created': plexpy.CONFIG.BOXCAR_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.BOXCAR_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.BOXCAR_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.BOXCAR_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.BOXCAR_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.BOXCAR_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.BOXCAR_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.BOXCAR_ON_NEWDEVICE
+ {'label': 'Boxcar2',
+ 'name': 'boxcar2',
+ 'id': AGENT_IDS['boxcar2']
},
- {'name': 'E-mail',
- 'id': AGENT_IDS['Email'],
- 'config_prefix': 'email',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.EMAIL_ENABLED),
- 'on_play': plexpy.CONFIG.EMAIL_ON_PLAY,
- 'on_stop': plexpy.CONFIG.EMAIL_ON_STOP,
- 'on_pause': plexpy.CONFIG.EMAIL_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.EMAIL_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.EMAIL_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.EMAIL_ON_WATCHED,
- 'on_created': plexpy.CONFIG.EMAIL_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.EMAIL_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.EMAIL_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.EMAIL_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.EMAIL_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.EMAIL_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.EMAIL_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.EMAIL_ON_NEWDEVICE
+ {'label': 'Email',
+ 'name': 'email',
+ 'id': AGENT_IDS['email']
},
- {'name': 'Twitter',
- 'id': AGENT_IDS['Twitter'],
- 'config_prefix': 'twitter',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.TWITTER_ENABLED),
- 'on_play': plexpy.CONFIG.TWITTER_ON_PLAY,
- 'on_stop': plexpy.CONFIG.TWITTER_ON_STOP,
- 'on_pause': plexpy.CONFIG.TWITTER_ON_PAUSE,
- '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_extdown': plexpy.CONFIG.TWITTER_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.TWITTER_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.TWITTER_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.TWITTER_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.TWITTER_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.TWITTER_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.TWITTER_ON_NEWDEVICE
+ {'label': 'Twitter',
+ 'name': 'twitter',
+ 'id': AGENT_IDS['twitter']
},
- {'name': 'IFTTT',
- 'id': AGENT_IDS['IFTTT'],
- 'config_prefix': 'ifttt',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.IFTTT_ENABLED),
- 'on_play': plexpy.CONFIG.IFTTT_ON_PLAY,
- 'on_stop': plexpy.CONFIG.IFTTT_ON_STOP,
- 'on_pause': plexpy.CONFIG.IFTTT_ON_PAUSE,
- '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_extdown': plexpy.CONFIG.IFTTT_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.IFTTT_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.IFTTT_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.IFTTT_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.IFTTT_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.IFTTT_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.IFTTT_ON_NEWDEVICE
+ {'label': 'IFTTT',
+ 'name': 'ifttt',
+ 'id': AGENT_IDS['ifttt']
},
- {'name': 'Telegram',
- 'id': AGENT_IDS['Telegram'],
- 'config_prefix': 'telegram',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.TELEGRAM_ENABLED),
- 'on_play': plexpy.CONFIG.TELEGRAM_ON_PLAY,
- 'on_stop': plexpy.CONFIG.TELEGRAM_ON_STOP,
- 'on_pause': plexpy.CONFIG.TELEGRAM_ON_PAUSE,
- '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_extdown': plexpy.CONFIG.TELEGRAM_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.TELEGRAM_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.TELEGRAM_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.TELEGRAM_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.TELEGRAM_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.TELEGRAM_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.TELEGRAM_ON_NEWDEVICE
+ {'label': 'Telegram',
+ 'name': 'telegram',
+ 'id': AGENT_IDS['telegram']
},
- {'name': 'Slack',
- 'id': AGENT_IDS['Slack'],
- 'config_prefix': 'slack',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.SLACK_ENABLED),
- 'on_play': plexpy.CONFIG.SLACK_ON_PLAY,
- 'on_stop': plexpy.CONFIG.SLACK_ON_STOP,
- 'on_resume': plexpy.CONFIG.SLACK_ON_RESUME,
- 'on_pause': plexpy.CONFIG.SLACK_ON_PAUSE,
- 'on_buffer': plexpy.CONFIG.SLACK_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.SLACK_ON_WATCHED,
- 'on_created': plexpy.CONFIG.SLACK_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.SLACK_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.SLACK_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.SLACK_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.SLACK_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.SLACK_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.SLACK_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.SLACK_ON_NEWDEVICE
+ {'label': 'Slack',
+ 'name': 'slack',
+ 'id': AGENT_IDS['slack']
},
- {'name': 'Scripts',
- 'id': AGENT_IDS['Scripts'],
- 'config_prefix': 'scripts',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.SCRIPTS_ENABLED),
- 'on_play': plexpy.CONFIG.SCRIPTS_ON_PLAY,
- 'on_stop': plexpy.CONFIG.SCRIPTS_ON_STOP,
- 'on_pause': plexpy.CONFIG.SCRIPTS_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.SCRIPTS_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.SCRIPTS_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.SCRIPTS_ON_WATCHED,
- 'on_created': plexpy.CONFIG.SCRIPTS_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.SCRIPTS_ON_EXTDOWN,
- 'on_extup': plexpy.CONFIG.SCRIPTS_ON_EXTUP,
- 'on_intdown': plexpy.CONFIG.SCRIPTS_ON_INTDOWN,
- 'on_intup': plexpy.CONFIG.SCRIPTS_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.SCRIPTS_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.SCRIPTS_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.SCRIPTS_ON_NEWDEVICE
+ {'label': 'Scripts',
+ 'name': 'scripts',
+ 'id': AGENT_IDS['scripts']
},
- {'name': 'Facebook',
- 'id': AGENT_IDS['Facebook'],
- 'config_prefix': 'facebook',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.FACEBOOK_ENABLED),
- 'on_play': plexpy.CONFIG.FACEBOOK_ON_PLAY,
- 'on_stop': plexpy.CONFIG.FACEBOOK_ON_STOP,
- 'on_pause': plexpy.CONFIG.FACEBOOK_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.FACEBOOK_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.FACEBOOK_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.FACEBOOK_ON_WATCHED,
- 'on_created': plexpy.CONFIG.FACEBOOK_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.FACEBOOK_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.FACEBOOK_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.FACEBOOK_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.FACEBOOK_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.FACEBOOK_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.FACEBOOK_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.FACEBOOK_ON_NEWDEVICE
+ {'label': 'Facebook',
+ 'name': 'facebook',
+ 'id': AGENT_IDS['facebook']
},
- {'name': 'Browser',
- 'id': AGENT_IDS['Browser'],
- 'config_prefix': 'browser',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.BROWSER_ENABLED),
- 'on_play': plexpy.CONFIG.BROWSER_ON_PLAY,
- 'on_stop': plexpy.CONFIG.BROWSER_ON_STOP,
- 'on_pause': plexpy.CONFIG.BROWSER_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.BROWSER_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.BROWSER_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.BROWSER_ON_WATCHED,
- 'on_created': plexpy.CONFIG.BROWSER_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.BROWSER_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.BROWSER_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.BROWSER_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.BROWSER_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.BROWSER_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.BROWSER_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.BROWSER_ON_NEWDEVICE
+ {'label': 'Browser',
+ 'name': 'browser',
+ 'id': AGENT_IDS['browser']
},
- {'name': 'Join',
- 'id': AGENT_IDS['Join'],
- 'config_prefix': 'join',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.JOIN_ENABLED),
- 'on_play': plexpy.CONFIG.JOIN_ON_PLAY,
- 'on_stop': plexpy.CONFIG.JOIN_ON_STOP,
- 'on_pause': plexpy.CONFIG.JOIN_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.JOIN_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.JOIN_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.JOIN_ON_WATCHED,
- 'on_created': plexpy.CONFIG.JOIN_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.JOIN_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.JOIN_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.JOIN_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.JOIN_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.JOIN_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.JOIN_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.JOIN_ON_NEWDEVICE
+ {'label': 'Join',
+ 'name': 'join',
+ 'id': AGENT_IDS['join']
},
- {'name': 'Hipchat',
- 'id': AGENT_IDS['Hipchat'],
- 'config_prefix': 'hipchat',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.HIPCHAT_ENABLED),
- 'on_play': plexpy.CONFIG.HIPCHAT_ON_PLAY,
- 'on_stop': plexpy.CONFIG.HIPCHAT_ON_STOP,
- 'on_pause': plexpy.CONFIG.HIPCHAT_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.HIPCHAT_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.HIPCHAT_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.HIPCHAT_ON_WATCHED,
- 'on_created': plexpy.CONFIG.HIPCHAT_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.HIPCHAT_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.HIPCHAT_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.HIPCHAT_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.HIPCHAT_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.HIPCHAT_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.HIPCHAT_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.HIPCHAT_ON_NEWDEVICE
+ {'label': 'Hipchat',
+ 'name': 'hipchat',
+ 'id': AGENT_IDS['hipchat']
}
]
# OSX Notifications should only be visible if it can be used
- osx_notify = OSX_NOTIFY()
- if osx_notify.validate():
- agents.append({'name': 'OSX Notify',
- 'id': AGENT_IDS['OSX Notify'],
- 'config_prefix': 'osx_notify',
- 'has_config': True,
- 'state': checked(plexpy.CONFIG.OSX_NOTIFY_ENABLED),
- 'on_play': plexpy.CONFIG.OSX_NOTIFY_ON_PLAY,
- 'on_stop': plexpy.CONFIG.OSX_NOTIFY_ON_STOP,
- 'on_pause': plexpy.CONFIG.OSX_NOTIFY_ON_PAUSE,
- 'on_resume': plexpy.CONFIG.OSX_NOTIFY_ON_RESUME,
- 'on_buffer': plexpy.CONFIG.OSX_NOTIFY_ON_BUFFER,
- 'on_watched': plexpy.CONFIG.OSX_NOTIFY_ON_WATCHED,
- 'on_created': plexpy.CONFIG.OSX_NOTIFY_ON_CREATED,
- 'on_extdown': plexpy.CONFIG.OSX_NOTIFY_ON_EXTDOWN,
- 'on_intdown': plexpy.CONFIG.OSX_NOTIFY_ON_INTDOWN,
- 'on_extup': plexpy.CONFIG.OSX_NOTIFY_ON_EXTUP,
- 'on_intup': plexpy.CONFIG.OSX_NOTIFY_ON_INTUP,
- 'on_pmsupdate': plexpy.CONFIG.OSX_NOTIFY_ON_PMSUPDATE,
- 'on_concurrent': plexpy.CONFIG.OSX_NOTIFY_ON_CONCURRENT,
- 'on_newdevice': plexpy.CONFIG.OSX_NOTIFY_ON_NEWDEVICE
+ if OSX().validate():
+ agents.append({'label': 'OSX Notify',
+ 'name': 'osx',
+ 'id': AGENT_IDS['OSX Notify']
})
return agents
-def get_notification_agent_config(agent_id):
+def available_notification_actions():
+ actions = [{'label': 'Playback Start',
+ 'name': 'on_play',
+ 'description': 'Trigger a notification when a stream is started.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': '{user} ({player}) started playing {title}.',
+ 'icon': 'fa-play'
+ },
+ {'label': 'Playback Stop',
+ 'name': 'on_stop',
+ 'description': 'Trigger a notification when a stream is stopped.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': '{user} ({player}) has stopped {title}.',
+ 'icon': 'fa-stop'
+ },
+ {'label': 'Playback Pause',
+ 'name': 'on_pause',
+ 'description': 'Trigger a notification when a stream is puased.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': '{user} ({player}) has paused {title}.',
+ 'icon': 'fa-pause'
+ },
+ {'label': 'Playback Resume',
+ 'name': 'on_resume',
+ 'description': 'Trigger a notification when a stream is resumed.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': '{user} ({player}) has resumed {title}.',
+ 'icon': 'fa-play'
+ },
+ {'label': 'Watched',
+ 'name': 'on_watched',
+ 'description': 'Trigger a notification when a video stream reaches the specified watch percentage.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': '{user} ({player}) has watched {title}.',
+ 'icon': 'fa-eye'
+ },
+ {'label': 'Buffer Warning',
+ 'name': 'on_buffer',
+ 'description': 'Trigger a notification when a stream exceeds the specified buffer threshold.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': '{user} ({player}) is buffering {title}.',
+ 'icon': 'fa-spinner'
+ },
+ {'label': 'User Concurrent Streams',
+ 'name': 'on_concurrent',
+ 'description': 'Trigger a notification when a user exceeds the concurrent stream threshold.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': '{user} has {user_streams} concurrent streams.',
+ 'icon': 'fa-arrow-circle-o-right'
+ },
+ {'label': 'User New Device',
+ 'name': 'on_newdevice',
+ 'description': 'Trigger a notification when a user streams from a new device.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': '{user} is streaming from a new device: {player}.',
+ 'icon': 'fa-desktop'
+ },
+ {'label': 'Recently Added',
+ 'name': 'on_created',
+ 'description': 'Trigger a notification when a media item is added to the Plex Media Server.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': '{title} was recently added to Plex.',
+ 'icon': 'fa-download'
+ },
+ {'label': 'Plex Server Down',
+ 'name': 'on_intdown',
+ 'description': 'Trigger a notification when the Plex Media Server cannot be reached internally.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': 'The Plex Media Server is down.',
+ 'icon': 'fa-server'
+ },
+ {'label': 'Plex Server Back Up',
+ 'name': 'on_intup',
+ 'description': 'Trigger a notification when the Plex Media Server can be reached internally after being down.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': 'The Plex Media Server is back up.',
+ 'icon': 'fa-server'
+ },
+ {'label': 'Plex Remote Access Down',
+ 'name': 'on_extdown',
+ 'description': 'Trigger a notification when the Plex Media Server cannot be reached externally.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': 'The Plex Media Server remote access is down.',
+ 'icon': 'fa-server'
+ },
+ {'label': 'Plex Remote Access Back Up',
+ 'name': 'on_extup',
+ 'description': 'Trigger a notification when the Plex Media Server can be reached externally after being down.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': 'The Plex Media Server remote access is back up.',
+ 'icon': 'fa-server'
+ },
+ {'label': 'Plex Update Available',
+ 'name': 'on_pmsupdate',
+ 'description': 'Trigger a notification when an update for the Plex Media Server is available.',
+ 'subject': 'PlexPy ({server_name})',
+ 'body': 'An update is available for the Plex Media Server (version {update_version}).',
+ 'icon': 'fa-refresh'
+ }
+ ]
+
+ return actions
+
+
+def get_agent_class(agent_id=None, config=None):
if str(agent_id).isdigit():
agent_id = int(agent_id)
if agent_id == 0:
- growl = GROWL()
- return growl.return_config_options()
+ return GROWL(config=config)
elif agent_id == 1:
- prowl = PROWL()
- return prowl.return_config_options()
+ return PROWL(config=config)
elif agent_id == 2:
- xbmc = XBMC()
- return xbmc.return_config_options()
+ return XBMC(config=config)
elif agent_id == 3:
- plex = Plex()
- return plex.return_config_options()
+ return PLEX(config=config)
elif agent_id == 4:
- nma = NMA()
- return nma.return_config_options()
+ return NMA(config=config)
elif agent_id == 5:
- pushalot = PUSHALOT()
- return pushalot.return_config_options()
+ return PUSHALOT(config=config)
elif agent_id == 6:
- pushbullet = PUSHBULLET()
- return pushbullet.return_config_options()
+ return PUSHBULLET(config=config)
elif agent_id == 7:
- pushover = PUSHOVER()
- return pushover.return_config_options()
+ return PUSHOVER(config=config)
elif agent_id == 8:
- osx_notify = OSX_NOTIFY()
- return osx_notify.return_config_options()
+ return OSX(config=config)
elif agent_id == 9:
- boxcar = BOXCAR()
- return boxcar.return_config_options()
+ return BOXCAR(config=config)
elif agent_id == 10:
- email = Email()
- return email.return_config_options()
+ return EMAIL(config=config)
elif agent_id == 11:
- tweet = TwitterNotifier()
- return tweet.return_config_options()
+ return TWITTER(config=config)
elif agent_id == 12:
- iftttClient = IFTTT()
- return iftttClient.return_config_options()
+ return IFTTT(config=config)
elif agent_id == 13:
- telegramClient = TELEGRAM()
- return telegramClient.return_config_options()
+ return TELEGRAM(config=config)
elif agent_id == 14:
- slackClient = SLACK()
- return slackClient.return_config_options()
+ return SLACK(config=config)
elif agent_id == 15:
- script = Scripts()
- return script.return_config_options()
+ return SCRIPTS(config=config)
elif agent_id == 16:
- facebook = FacebookNotifier()
- return facebook.return_config_options()
+ return FACEBOOK(config=config)
elif agent_id == 17:
- browser = Browser()
- return browser.return_config_options()
+ return BROWSER(config=config)
elif agent_id == 18:
- join = JOIN()
- return join.return_config_options()
+ return JOIN(config=config)
elif agent_id == 19:
- hipchat = HIPCHAT()
- return hipchat.return_config_options()
+ return HIPCHAT(config=config)
else:
- return []
+ return Notifier(config=config)
else:
- return []
+ return None
-def send_notification(agent_id, subject, body, notify_action, **kwargs):
+def get_notify_actions():
+ return tuple(a['name'] for a in available_notification_actions())
+
+
+def get_notifiers(notifier_id=None, notify_action=None):
+ notify_actions = get_notify_actions()
+
+ where_action = ''
+ if notify_action and notify_action in notify_actions:
+ where_action = 'WHERE %s = 1 ' % notify_action
+
+ monitor_db = database.MonitorDatabase()
+ result = monitor_db.select('SELECT id, agent_id, agent_name, agent_label, friendly_name, %s FROM notifiers %s'
+ % (', '.join(notify_actions), where_action))
+
+ for item in result:
+ item['actions'] = {k: helpers.cast_to_int(item.pop(k))
+ for k in item.keys() if k in notify_actions}
+
+ return sorted(result, key=lambda k: (k['agent_label'], k['id']))
+
+
+def delete_notifier(notifier_id=None):
+ monitor_db = database.MonitorDatabase()
+
+ if str(notifier_id).isdigit():
+ logger.debug(u"PlexPy Notifiers :: Deleting notifier_id %s from the database." % notifier_id)
+ result = monitor_db.action('DELETE FROM notifiers WHERE id = ?', [notifier_id])
+ return True
+ else:
+ return False
+
+
+def get_notifier_config(notifier_id=None):
+ if str(notifier_id).isdigit():
+ notifier_id = int(notifier_id)
+ else:
+ logger.error(u"PlexPy Notifiers :: Unable to retrieve notifier config: invalid notifier_id.")
+ return None
+
+ monitor_db = database.MonitorDatabase()
+ result = monitor_db.select_single('SELECT * FROM notifiers WHERE id = ?',
+ args=[notifier_id])
+
+ if not result:
+ return None
+
+ config = json.loads(result.pop('notifier_config') or '{}')
+ notifier_agent = get_agent_class(agent_id=result['agent_id'], config=config)
+ notifier_config = notifier_agent.return_config_options()
+
+ notify_actions = get_notify_actions()
+
+ notifier_actions = {}
+ notifier_text = {}
+ for k in result.keys():
+ if k in notify_actions:
+ notifier_actions[k] = helpers.cast_to_int(result.pop(k))
+ notifier_text[k] = {'subject': result.pop(k + '_subject'),
+ 'body': result.pop(k + '_body')}
+
+ result['agent'] = notifier_agent
+ result['config'] = notifier_config
+ result['actions'] = notifier_actions
+ result['notify_text'] = notifier_text
+
+ return result
+
+
+def add_notifier_config(agent_id=None, **kwargs):
if str(agent_id).isdigit():
agent_id = int(agent_id)
-
- if agent_id == 0:
- growl = GROWL()
- return growl.notify(message=body, event=subject)
- elif agent_id == 1:
- prowl = PROWL()
- return prowl.notify(message=body, event=subject)
- elif agent_id == 2:
- xbmc = XBMC()
- return xbmc.notify(subject=subject, message=body)
- elif agent_id == 3:
- plex = Plex()
- return plex.notify(subject=subject, message=body)
- elif agent_id == 4:
- nma = NMA()
- return nma.notify(subject=subject, message=body)
- elif agent_id == 5:
- pushalot = PUSHALOT()
- return pushalot.notify(message=body, event=subject)
- elif agent_id == 6:
- pushbullet = PUSHBULLET()
- return pushbullet.notify(message=body, subject=subject)
- elif agent_id == 7:
- pushover = PUSHOVER()
- return pushover.notify(message=body, event=subject, **kwargs)
- elif agent_id == 8:
- osx_notify = OSX_NOTIFY()
- return osx_notify.notify(title=subject, text=body)
- elif agent_id == 9:
- boxcar = BOXCAR()
- return boxcar.notify(title=subject, message=body)
- elif agent_id == 10:
- email = Email()
- return email.notify(subject=subject, message=body)
- elif agent_id == 11:
- twitter = TwitterNotifier()
- return twitter.notify(subject=subject, message=body, **kwargs)
- elif agent_id == 12:
- iftttClient = IFTTT()
- return iftttClient.notify(subject=subject, message=body, action=notify_action)
- elif agent_id == 13:
- telegramClient = TELEGRAM()
- return telegramClient.notify(message=body, event=subject, **kwargs)
- elif agent_id == 14:
- slackClient = SLACK()
- return slackClient.notify(message=body, event=subject, **kwargs)
- elif agent_id == 15:
- scripts = Scripts()
- return scripts.notify(message=body, subject=subject, notify_action=notify_action, **kwargs)
- elif agent_id == 16:
- facebook = FacebookNotifier()
- return facebook.notify(subject=subject, message=body, **kwargs)
- elif agent_id == 17:
- browser = Browser()
- return browser.notify(subject=subject, message=body)
- elif agent_id == 18:
- join = JOIN()
- return join.notify(message=body, subject=subject)
- elif agent_id == 19:
- hipchat = HIPCHAT()
- return hipchat.notify(message=body, subject=subject, **kwargs)
- else:
- logger.debug(u"PlexPy Notifiers :: Unknown agent id received.")
else:
- logger.debug(u"PlexPy Notifiers :: Notification requested but no agent id received.")
+ logger.error(u"PlexPy Notifiers :: Unable to add new notifier: invalid agent_id.")
+ return False
+
+ agent = next((a for a in available_notification_agents() if a['id'] == agent_id), None)
+
+ if not agent:
+ logger.error(u"PlexPy Notifiers :: Unable to retrieve new notification agent: invalid agent_id.")
+ return False
+
+ keys = {'id': None}
+ values = {'agent_id': agent['id'],
+ 'agent_name': agent['name'],
+ 'agent_label': agent['label'],
+ 'friendly_name': '',
+ 'notifier_config': json.dumps(get_agent_class(agent_id=agent['id']).config)
+ }
+ if agent['name'] == 'scripts':
+ for a in available_notification_actions():
+ values[a['name'] + '_subject'] = ''
+ values[a['name'] + '_body'] = ''
+ else:
+ for a in available_notification_actions():
+ values[a['name'] + '_subject'] = a['subject']
+ values[a['name'] + '_body'] = a['body']
+
+ monitor_db = database.MonitorDatabase()
+ try:
+ monitor_db.upsert(table_name='notifiers', key_dict=keys, value_dict=values)
+ logger.info(u"PlexPy Notifiers :: Added new notification agent: %s." % agent['label'])
+ return True
+ except Exception as e:
+ logger.warn(u"PlexPy Notifiers :: Unable to add notification agent: %s." % e)
+ return False
+
+
+def set_notifier_config(notifier_id=None, agent_id=None, **kwargs):
+ if str(agent_id).isdigit():
+ agent_id = int(agent_id)
+ else:
+ logger.error(u"PlexPy Notifiers :: Unable to set exisiting notifier: invalid agent_id.")
+ return False
+
+ agent = next((a for a in available_notification_agents() if a['id'] == agent_id), None)
+
+ if not agent:
+ logger.error(u"PlexPy Notifiers :: Unable to retrieve existing notification agent: invalid agent_id.")
+ return False
+
+ notify_actions = get_notify_actions()
+ config_prefix = agent['name'] + '_'
+
+ actions = {k: helpers.cast_to_int(kwargs.pop(k))
+ for k in kwargs.keys() if k in notify_actions}
+ subject_text = {k: kwargs.pop(k)
+ for k in kwargs.keys() if k.startswith(notify_actions) and k.endswith('_subject')}
+ body_text = {k: kwargs.pop(k)
+ for k in kwargs.keys() if k.startswith(notify_actions) and k.endswith('_body')}
+ notifier_config = {k[len(config_prefix):]: kwargs.pop(k)
+ for k in kwargs.keys() if k.startswith(config_prefix)}
+ notifier_config = get_agent_class(agent['id']).set_config(config=notifier_config)
+
+ keys = {'id': notifier_id}
+ values = {'agent_id': agent['id'],
+ 'agent_name': agent['name'],
+ 'agent_label': agent['label'],
+ 'friendly_name': kwargs.get('friendly_name', ''),
+ 'notifier_config': json.dumps(notifier_config),
+ }
+ values.update(actions)
+ values.update(subject_text)
+ values.update(body_text)
+
+ monitor_db = database.MonitorDatabase()
+ try:
+ monitor_db.upsert(table_name='notifiers', key_dict=keys, value_dict=values)
+ logger.info(u"PlexPy Notifiers :: Updated notification agent: %s." % agent['label'])
+ return True
+ except Exception as e:
+ logger.warn(u"PlexPy Notifiers :: Unable to update notification agent: %s." % e)
+ return False
+
+
+def send_notification(notifier_id=None, subject='', body='', notify_action='', **kwargs):
+ notifier_config = get_notifier_config(notifier_id=notifier_id)
+ if notifier_config:
+ agent = notifier_config['agent']
+ return agent.notify(subject=subject,
+ body=body,
+ action=notify_action,
+ **kwargs)
+ else:
+ logger.debug(u"PlexPy Notifiers :: Notification requested but no notifier_id received.")
class PrettyMetadata(object):
@@ -686,37 +547,63 @@ class PrettyMetadata(object):
return self.plex_url
-class GROWL(object):
+class Notifier(object):
+ _DEFAULT_CONFIG = {}
+
+ def __init__(self, config=None):
+ self.set_config(config)
+
+ def set_config(self, config=None):
+ self.config = self._validate_config(config)
+ return self.config
+
+ def _validate_config(self, config=None):
+ if config is None:
+ return self._DEFAULT_CONFIG
+
+ new_config = {}
+ for k, v in self._DEFAULT_CONFIG.iteritems():
+ if isinstance(v, int):
+ new_config[k] = helpers.cast_to_int(config.get(k, v))
+ else:
+ new_config[k] = config.get(k, v)
+
+ return new_config
+
+ def notify(self, subject='', body='', action='', **kwargs):
+ pass
+
+ def return_config_options(self):
+ config_options = []
+ return config_options
+
+
+class GROWL(Notifier):
"""
Growl notifications, for OS X.
"""
+ _DEFAULT_CONFIG = {'host': '',
+ 'password': ''
+ }
- def __init__(self):
- self.enabled = plexpy.CONFIG.GROWL_ENABLED
- self.host = plexpy.CONFIG.GROWL_HOST
- self.password = plexpy.CONFIG.GROWL_PASSWORD
-
- def conf(self, options):
- return cherrypy.config['config'].get('Growl', options)
-
- def notify(self, message, event):
- if not message or not event:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
# Split host and port
- if self.host == "":
+ if self.config['host'] == "":
host, port = "localhost", 23053
- if ":" in self.host:
- host, port = self.host.split(':', 1)
+ if ":" in self.config['host']:
+ host, port = self.config['host'].split(':', 1)
port = int(port)
else:
- host, port = self.host, 23053
+ host, port = self.config['host'], 23053
# If password is empty, assume none
- if self.password == "":
+ if self.config['password'] == "":
password = None
else:
- password = self.password
+ password = self.config['password']
# Register notification
growl = gntp.notifier.GrowlNotifier(
@@ -738,7 +625,7 @@ class GROWL(object):
return False
# Fix message
- message = message.encode(plexpy.SYS_ENCODING, "replace")
+ body = body.encode(plexpy.SYS_ENCODING, "replace")
# Send it, including an image
image_file = os.path.join(str(plexpy.PROG_DIR),
@@ -750,8 +637,8 @@ class GROWL(object):
try:
growl.notify(
noteType='New Event',
- title=event,
- description=message,
+ title=subject,
+ description=body,
icon=image
)
logger.info(u"PlexPy Notifiers :: Growl notification sent.")
@@ -760,27 +647,15 @@ class GROWL(object):
logger.warn(u"PlexPy Notifiers :: Growl notification failed: network error")
return False
-
- def updateLibrary(self):
- # For uniformity reasons not removed
- return
-
- def test(self, host, password):
- self.enabled = True
- self.host = host
- self.password = password
-
- self.notify('ZOMG Lazors Pewpewpew!', 'Test Message')
-
def return_config_options(self):
config_option = [{'label': 'Growl Host',
- 'value': self.host,
+ 'value': self.config['host'],
'name': 'growl_host',
'description': 'Your Growl hostname.',
'input_type': 'text'
},
{'label': 'Growl Password',
- 'value': self.password,
+ 'value': self.config['password'],
'name': 'growl_password',
'description': 'Your Growl password.',
'input_type': 'password'
@@ -790,36 +665,29 @@ class GROWL(object):
return config_option
-class PROWL(object):
+class PROWL(Notifier):
"""
Prowl notifications.
"""
+ _DEFAULT_CONFIG = {'keys': '',
+ 'priority': 0
+ }
- def __init__(self):
- self.enabled = plexpy.CONFIG.PROWL_ENABLED
- self.keys = plexpy.CONFIG.PROWL_KEYS
- self.priority = plexpy.CONFIG.PROWL_PRIORITY
-
- def conf(self, options):
- return cherrypy.config['config'].get('Prowl', options)
-
- def notify(self, message, event):
- if not message or not event:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
- http_handler = HTTPSConnection("api.prowlapp.com")
-
- data = {'apikey': self.keys,
+ data = {'apikey': self.config['keys'],
'application': 'PlexPy',
- 'event': event.encode("utf-8"),
- 'description': message.encode("utf-8"),
- 'priority': self.priority}
+ 'event': subject.encode("utf-8"),
+ 'description': body.encode("utf-8"),
+ 'priority': self.config['priority']}
+ http_handler = HTTPSConnection("api.prowlapp.com")
http_handler.request("POST",
"/publicapi/add",
headers={'Content-type': "application/x-www-form-urlencoded"},
body=urlencode(data))
-
response = http_handler.getresponse()
request_status = response.status
@@ -833,26 +701,15 @@ class PROWL(object):
logger.warn(u"PlexPy Notifiers :: Prowl notification failed.")
return False
- def updateLibrary(self):
- # For uniformity reasons not removed
- return
-
- def test(self, keys, priority):
- self.enabled = True
- self.keys = keys
- self.priority = priority
-
- self.notify('ZOMG Lazors Pewpewpew!', 'Test Message')
-
def return_config_options(self):
config_option = [{'label': 'Prowl API Key',
- 'value': self.keys,
+ 'value': self.config['keys'],
'name': 'prowl_keys',
'description': 'Your Prowl API key.',
'input_type': 'text'
},
{'label': 'Priority',
- 'value': self.priority,
+ 'value': self.config['priority'],
'name': 'prowl_priority',
'description': 'Set the priority.',
'input_type': 'select',
@@ -863,22 +720,21 @@ class PROWL(object):
return config_option
-class XBMC(object):
+class XBMC(Notifier):
"""
XBMC notifications
"""
-
- def __init__(self):
- self.hosts = plexpy.CONFIG.XBMC_HOST
- self.username = plexpy.CONFIG.XBMC_USERNAME
- self.password = plexpy.CONFIG.XBMC_PASSWORD
+ _DEFAULT_CONFIG = {'hosts': '',
+ 'username': '',
+ 'password': ''
+ }
def _sendhttp(self, host, command):
url_command = urllib.urlencode(command)
url = host + '/xbmcCmds/xbmcHttp/?' + url_command
- if self.password:
- return request.request_content(url, auth=(self.username, self.password))
+ if self.config['password']:
+ return request.request_content(url, auth=(self.config['username'], self.config['password']))
else:
return request.request_content(url)
@@ -887,21 +743,22 @@ class XBMC(object):
headers = {'Content-Type': 'application/json'}
url = host + '/jsonrpc'
- if self.password:
- response = request.request_json(url, method="post", data=json.dumps(data), headers=headers, auth=(self.username, self.password))
+ if self.config['password']:
+ response = request.request_json(url, method="post", data=json.dumps(data), headers=headers,
+ auth=(self.config['username'], self.config['password']))
else:
response = request.request_json(url, method="post", data=json.dumps(data), headers=headers)
if response:
return response[0]['result']
- def notify(self, subject=None, message=None):
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
+ return
- hosts = [x.strip() for x in self.hosts.split(',')]
+ hosts = [x.strip() for x in self.config['hosts'].split(',')]
- header = subject
- message = message
- time = "3000" # in ms
+ display_time = "3000" # in ms
for host in hosts:
logger.info(u"PlexPy Notifiers :: Sending notification command to XMBC @ " + host)
@@ -909,12 +766,12 @@ class XBMC(object):
version = self._sendjson(host, 'Application.GetProperties', {'properties': ['version']})['version']['major']
if version < 12: # Eden
- notification = header + "," + message + "," + time
+ notification = subject + "," + body + "," + display_time
notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'}
request = self._sendhttp(host, notifycommand)
else: # Frodo
- params = {'title': header, 'message': message, 'displaytime': int(time)}
+ params = {'title': subject, 'message': body, 'displaytime': int(display_time)}
request = self._sendjson(host, 'GUI.ShowNotification', params)
if not request:
@@ -930,19 +787,19 @@ class XBMC(object):
def return_config_options(self):
config_option = [{'label': 'XBMC Host:Port',
- 'value': self.hosts,
- 'name': 'xbmc_host',
+ 'value': self.config['hosts'],
+ 'name': 'xbmc_hosts',
'description': 'Host running XBMC (e.g. http://localhost:8080). Separate multiple hosts with commas (,).',
'input_type': 'text'
},
{'label': 'XBMC Username',
- 'value': self.username,
+ 'value': self.config['username'],
'name': 'xbmc_username',
'description': 'Username of your XBMC client API (blank for none).',
'input_type': 'text'
},
{'label': 'XBMC Password',
- 'value': self.password,
+ 'value': self.config['password'],
'name': 'xbmc_password',
'description': 'Password of your XBMC client API (blank for none).',
'input_type': 'password'
@@ -952,18 +809,21 @@ class XBMC(object):
return config_option
-class Plex(object):
- def __init__(self):
- self.client_hosts = plexpy.CONFIG.PLEX_CLIENT_HOST
- self.username = plexpy.CONFIG.PLEX_USERNAME
- self.password = plexpy.CONFIG.PLEX_PASSWORD
+class PLEX(Notifier):
+ """
+ Plex Home Theater notifications
+ """
+ _DEFAULT_CONFIG = {'client_hosts': '',
+ 'username': '',
+ 'password': ''
+ }
def _sendhttp(self, host, command):
url_command = urllib.urlencode(command)
url = host + '/xbmcCmds/xbmcHttp/?' + url_command
- if self.password:
- return request.request_content(url, auth=(self.username, self.password))
+ if self.config['password']:
+ return request.request_content(url, auth=(self.config['username'], self.config['password']))
else:
return request.request_content(url)
@@ -972,21 +832,22 @@ class Plex(object):
headers = {'Content-Type': 'application/json'}
url = host + '/jsonrpc'
- if self.password:
- response = request.request_json(url, method="post", data=json.dumps(data), headers=headers, auth=(self.username, self.password))
+ if self.config['password']:
+ response = request.request_json(url, method="post", data=json.dumps(data), headers=headers,
+ auth=(self.config['username'], self.config['password']))
else:
response = request.request_json(url, method="post", data=json.dumps(data), headers=headers)
if response:
return response[0]['result']
- def notify(self, subject=None, message=None):
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
+ return
- hosts = [x.strip() for x in self.client_hosts.split(',')]
+ hosts = [x.strip() for x in self.config['client_hosts'].split(',')]
- header = subject
- message = message
- time = "3000" # in ms
+ display_time = "3000" # in ms
for host in hosts:
logger.info(u"PlexPy Notifiers :: Sending notification command to Plex Home Theater @ " + host)
@@ -994,12 +855,12 @@ class Plex(object):
version = self._sendjson(host, 'Application.GetProperties', {'properties': ['version']})['version']['major']
if version < 12: # Eden
- notification = header + "," + message + "," + time
+ notification = subject + "," + body + "," + display_time
notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'}
request = self._sendhttp(host, notifycommand)
else: # Frodo
- params = {'title': header, 'message': message, 'displaytime': int(time)}
+ params = {'title': subject, 'message': body, 'displaytime': int(display_time)}
request = self._sendjson(host, 'GUI.ShowNotification', params)
if not request:
@@ -1015,19 +876,19 @@ class Plex(object):
def return_config_options(self):
config_option = [{'label': 'Plex Home Theater Host:Port',
- 'value': self.client_hosts,
- 'name': 'plex_client_host',
+ 'value': self.config['client_hosts'],
+ 'name': 'plex_client_hosts',
'description': 'Host running Plex Home Theater (eg. http://localhost:3005). Separate multiple hosts with commas (,).',
'input_type': 'text'
},
{'label': 'Plex Home Theater Username',
- 'value': self.username,
+ 'value': self.config['username'],
'name': 'plex_username',
'description': 'Username of your Plex Home Theater client API (blank for none).',
'input_type': 'text'
},
{'label': 'Plex Home Theater Password',
- 'value': self.password,
+ 'value': self.config['password'],
'name': 'plex_password',
'description': 'Password of your Plex Home Theater client API (blank for none).',
'input_type': 'password'
@@ -1037,39 +898,31 @@ class Plex(object):
return config_option
-class NMA(object):
+class NMA(Notifier):
+ """
+ Notify My Android notifications
+ """
+ _DEFAULT_CONFIG = {'apikey': '',
+ 'priority': 0
+ }
- def __init__(self):
- self.apikey = plexpy.CONFIG.NMA_APIKEY
- self.priority = plexpy.CONFIG.NMA_PRIORITY
-
- def notify(self, subject=None, message=None):
- if not subject or not message:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
title = 'PlexPy'
-
- # 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)
-
batch = False
p = pynma.PyNMA()
- keys = self.apikey.split(',')
+ keys = self.config['apikey'].split(',')
p.addkey(keys)
if len(keys) > 1:
batch = True
- response = p.push(title, event, message, priority=self.priority, batch_mode=batch)
+ response = p.push(title, subject, body, priority=self.config['priority'], batch_mode=batch)
- if not response[self.apikey][u'code'] == u'200':
+ if not response[self.config['apikey']][u'code'] == u'200':
logger.warn(u"PlexPy Notifiers :: NotifyMyAndroid notification failed.")
return False
else:
@@ -1078,13 +931,13 @@ class NMA(object):
def return_config_options(self):
config_option = [{'label': 'NotifyMyAndroid API Key',
- 'value': self.apikey,
+ 'value': self.config['apikey'],
'name': 'nma_apikey',
'description': 'Your NotifyMyAndroid API key. Separate multiple api keys with commas.',
'input_type': 'text'
},
{'label': 'Priority',
- 'value': self.priority,
+ 'value': self.config['priority'],
'name': 'nma_priority',
'description': 'Set the priority.',
'input_type': 'select',
@@ -1095,42 +948,82 @@ class NMA(object):
return config_option
-class PUSHBULLET(object):
+class PUSHALOT(Notifier):
+ """
+ Pushalot notifications
+ """
+ _DEFAULT_CONFIG = {'apikey': ''
+ }
- def __init__(self):
- self.apikey = plexpy.CONFIG.PUSHBULLET_APIKEY
- self.deviceid = plexpy.CONFIG.PUSHBULLET_DEVICEID
- self.channel_tag = plexpy.CONFIG.PUSHBULLET_CHANNEL_TAG
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
+ return
- def conf(self, options):
- return cherrypy.config['config'].get('PUSHBULLET', options)
+ data = {'AuthorizationToken': self.config['apikey'],
+ 'Title': subject.encode('utf-8'),
+ 'Body': body.encode("utf-8")}
- def notify(self, message, subject):
- if not message or not subject:
+ http_handler = HTTPSConnection("pushalot.com")
+ http_handler.request("POST",
+ "/api/sendmessage",
+ headers={'Content-type': "application/x-www-form-urlencoded"},
+ body=urlencode(data))
+ response = http_handler.getresponse()
+ request_status = response.status
+
+ if request_status == 200:
+ logger.info(u"PlexPy Notifiers :: Pushalot notification sent.")
+ return True
+ elif request_status == 410:
+ logger.warn(u"PlexPy Notifiers :: Pushalot notification failed: [%s] %s" % (request_status, response.reason))
+ return False
+ else:
+ logger.warn(u"PlexPy Notifiers :: Pushalot notification failed.")
+ return False
+
+ def return_config_options(self):
+ config_option = [{'label': 'Pushalot API Key',
+ 'value': self.config['apikey'],
+ 'name': 'pushalot_apikey',
+ 'description': 'Your Pushalot API key.',
+ 'input_type': 'text'
+ }
+ ]
+
+ return config_option
+
+
+class PUSHBULLET(Notifier):
+ """
+ Pushbullet notifications
+ """
+ _DEFAULT_CONFIG = {'apikey': '',
+ 'deviceid': '',
+ 'channel_tag': ''
+ }
+
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
data = {'type': "note",
'title': subject.encode("utf-8"),
- 'body': message.encode("utf-8")}
+ 'body': body.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
+ if self.config['deviceid']:
+ data['device_iden'] = self.config['deviceid']
+ elif self.config['channel_tag']:
+ data['channel_tag'] = self.config['channel_tag']
http_handler = HTTPSConnection("api.pushbullet.com")
http_handler.request("POST",
"/v2/pushes",
headers={'Content-type': "application/json",
- 'Authorization': 'Basic %s' % base64.b64encode(self.apikey + ":")},
+ 'Authorization': 'Basic %s' % base64.b64encode(self.config['apikey'] + ":")},
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())
if request_status == 200:
logger.info(u"PlexPy Notifiers :: PushBullet notification sent.")
@@ -1142,20 +1035,12 @@ class PUSHBULLET(object):
logger.warn(u"PlexPy Notifiers :: PushBullet notification failed.")
return False
- def test(self, apikey, deviceid):
-
- self.enabled = True
- self.apikey = apikey
- self.deviceid = deviceid
-
- self.notify('Main Screen Activate', 'Test Message')
-
def get_devices(self):
- if self.apikey:
+ if self.config['apikey']:
http_handler = HTTPSConnection("api.pushbullet.com")
http_handler.request("GET", "/v2/devices",
headers={'Content-type': "application/json",
- 'Authorization': 'Basic %s' % base64.b64encode(self.apikey + ":")})
+ 'Authorization': 'Basic %s' % base64.b64encode(self.config['apikey'] + ":")})
response = http_handler.getresponse()
request_status = response.status
@@ -1178,13 +1063,14 @@ class PUSHBULLET(object):
def return_config_options(self):
config_option = [{'label': 'Pushbullet API Key',
- 'value': self.apikey,
+ 'value': self.config['apikey'],
'name': 'pushbullet_apikey',
'description': 'Your Pushbullet API key.',
- 'input_type': 'text'
+ 'input_type': 'text',
+ 'refresh': True
},
{'label': 'Device',
- 'value': self.deviceid,
+ 'value': self.config['deviceid'],
'name': 'pushbullet_deviceid',
'description': 'Set your Pushbullet device. If set, will override channel tag. ' \
'Leave blank to notify on all devices.',
@@ -1192,7 +1078,7 @@ class PUSHBULLET(object):
'select_options': self.get_devices()
},
{'label': 'Channel',
- 'value': self.channel_tag,
+ 'value': self.config['channel_tag'],
'name': 'pushbullet_channel_tag',
'description': 'A channel tag (optional).',
'input_type': 'text'
@@ -1202,79 +1088,23 @@ class PUSHBULLET(object):
return config_option
-class PUSHALOT(object):
+class PUSHOVER(Notifier):
+ """
+ Pushover notifications
+ """
+ _DEFAULT_CONFIG = {'apitoken': '',
+ 'keys': '',
+ 'html_support': 1,
+ 'priority': 0,
+ 'sound': '',
+ 'incl_url': 1,
+ 'incl_pmslink': 0
+ }
- def __init__(self):
- self.apikey = plexpy.CONFIG.PUSHALOT_APIKEY
-
- def notify(self, message, event):
- if not message or not event:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
- # logger.debug(u"Pushalot event: " + event)
- # logger.debug(u"Pushalot message: " + message)
- # logger.debug(u"Pushalot api: " + self.api_key)
-
- http_handler = HTTPSConnection("pushalot.com")
-
- data = {'AuthorizationToken': self.apikey,
- 'Title': event.encode('utf-8'),
- 'Body': message.encode("utf-8")}
-
- http_handler.request("POST",
- "/api/sendmessage",
- headers={'Content-type': "application/x-www-form-urlencoded"},
- body=urlencode(data))
- response = http_handler.getresponse()
- request_status = response.status
-
- # logger.debug(u"Pushalot response status: %r" % request_status)
- # logger.debug(u"Pushalot response headers: %r" % response.getheaders())
- # logger.debug(u"Pushalot response body: %r" % response.read())
-
- if request_status == 200:
- logger.info(u"PlexPy Notifiers :: Pushalot notification sent.")
- return True
- elif request_status == 410:
- logger.warn(u"PlexPy Notifiers :: Pushalot notification failed: [%s] %s" % (request_status, response.reason))
- return False
- else:
- logger.warn(u"PlexPy Notifiers :: Pushalot notification failed.")
- return False
-
- def return_config_options(self):
- config_option = [{'label': 'Pushalot API Key',
- 'value': self.apikey,
- 'name': 'pushalot_apikey',
- 'description': 'Your Pushalot API key.',
- 'input_type': 'text'
- }
- ]
-
- return config_option
-
-
-class PUSHOVER(object):
-
- def __init__(self):
- self.enabled = plexpy.CONFIG.PUSHOVER_ENABLED
- self.apitoken = plexpy.CONFIG.PUSHOVER_APITOKEN
- self.keys = plexpy.CONFIG.PUSHOVER_KEYS
- self.html_support = plexpy.CONFIG.PUSHOVER_HTML_SUPPORT
- self.priority = plexpy.CONFIG.PUSHOVER_PRIORITY
- self.sound = plexpy.CONFIG.PUSHOVER_SOUND
- self.incl_pmslink = plexpy.CONFIG.PUSHOVER_INCL_PMSLINK
- self.incl_url = plexpy.CONFIG.PUSHOVER_INCL_URL
-
- def conf(self, options):
- return cherrypy.config['config'].get('Pushover', options)
-
- def notify(self, message, event, **kwargs):
- if not message or not event:
- return
-
- http_handler = HTTPSConnection("api.pushover.net")
-
data = {'token': self.apitoken,
'user': self.keys,
'title': event.encode("utf-8"),
@@ -1283,29 +1113,27 @@ class PUSHOVER(object):
'html': self.html_support,
'priority': self.priority}
- if self.incl_url and 'metadata' in kwargs:
+ if self.config['incl_url'] and 'metadata' in kwargs:
# Grab formatted metadata
pretty_metadata = PrettyMetadata(kwargs['metadata'])
plex_url = pretty_metadata.get_plex_url()
poster_link = pretty_metadata.get_poster_link()
caption = pretty_metadata.get_caption()
- if self.incl_pmslink:
+ if self.config['incl_pmslink']:
data['url'] = plex_url
data['url_title'] = 'View on Plex Web'
else:
data['url'] = poster_link
data['url_title'] = caption
+ http_handler = HTTPSConnection("api.pushover.net")
http_handler.request("POST",
"/1/messages.json",
headers={'Content-type': "application/x-www-form-urlencoded"},
body=urlencode(data))
response = http_handler.getresponse()
request_status = response.status
- # logger.debug(u"Pushover response status: %r" % request_status)
- # logger.debug(u"Pushover response headers: %r" % response.getheaders())
- # logger.debug(u"Pushover response body: %r" % response.read())
if request_status == 200:
logger.info(u"PlexPy Notifiers :: Pushover notification sent.")
@@ -1317,23 +1145,10 @@ class PUSHOVER(object):
logger.warn(u"PlexPy Notifiers :: Pushover notification failed.")
return False
- def updateLibrary(self):
- # For uniformity reasons not removed
- return
-
- def test(self, keys, priority, sound, html_support):
- self.enabled = True
- self.keys = keys
- self.priority = priority
- self.sound = sound
- self.html_support = html_support
-
- self.notify('Main Screen Activate', 'Test Message')
-
def get_sounds(self):
- if self.apitoken:
+ if self.config['apitoken']:
http_handler = HTTPSConnection("api.pushover.net")
- http_handler.request("GET", "/1/sounds.json?token=" + self.apitoken)
+ http_handler.request("GET", "/1/sounds.json?token=" + self.config['apitoken'])
response = http_handler.getresponse()
request_status = response.status
@@ -1354,45 +1169,46 @@ class PUSHOVER(object):
def return_config_options(self):
config_option = [{'label': 'Pushover API Token',
- 'value': self.apitoken,
+ 'value': self.config['apitoken'],
'name': 'pushover_apitoken',
'description': 'Your Pushover API token.',
- 'input_type': 'text'
+ 'input_type': 'text',
+ 'refresh': True
},
{'label': 'Pushover User or Group Key',
- 'value': self.keys,
+ 'value': self.config['keys'],
'name': 'pushover_keys',
'description': 'Your Pushover user or group key.',
'input_type': 'text'
},
{'label': 'Priority',
- 'value': self.priority,
+ 'value': self.config['priority'],
'name': 'pushover_priority',
'description': 'Set the priority.',
'input_type': 'select',
'select_options': {-2: -2, -1: -1, 0: 0, 1: 1, 2: 2}
},
{'label': 'Sound',
- 'value': self.sound,
+ 'value': self.config['sound'],
'name': 'pushover_sound',
'description': 'Set the notification sound. Leave blank for the default sound.',
'input_type': 'select',
'select_options': self.get_sounds()
},
{'label': 'Enable HTML Support',
- 'value': self.html_support,
+ 'value': self.config['html_support'],
'name': 'pushover_html_support',
'description': 'Style your messages using these HTML tags: b, i, u, a[href], font[color]',
'input_type': 'checkbox'
},
{'label': 'Include supplementary URL',
- 'value': self.incl_url,
+ 'value': self.config['incl_url'],
'name': 'pushover_incl_url',
'description': 'Include a supplementary URL to IMDB, TVDB, TMDb, or Last.fm with the notifications.',
'input_type': 'checkbox'
},
{'label': 'Supplementary URL to Plex Web',
- 'value': self.incl_pmslink,
+ 'value': self.config['incl_pmslink'],
'name': 'pushover_incl_pmslink',
'description': 'Enable to change the supplementary URL to the media in Plex Web.',
'input_type': 'checkbox'
@@ -1402,213 +1218,27 @@ class PUSHOVER(object):
return config_option
-class TwitterNotifier(object):
+class BOXCAR(Notifier):
+ """
+ Boxcar notifications
+ """
+ _DEFAULT_CONFIG = {'token': '',
+ 'sound': ''
+ }
- REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token'
- ACCESS_TOKEN_URL = 'https://api.twitter.com/oauth/access_token'
- AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize'
- SIGNIN_URL = 'https://api.twitter.com/oauth/authenticate'
-
- def __init__(self):
- self.access_token = plexpy.CONFIG.TWITTER_ACCESS_TOKEN
- self.access_token_secret = plexpy.CONFIG.TWITTER_ACCESS_TOKEN_SECRET
- self.consumer_key = plexpy.CONFIG.TWITTER_CONSUMER_KEY
- self.consumer_secret = plexpy.CONFIG.TWITTER_CONSUMER_SECRET
- self.incl_poster = plexpy.CONFIG.TWITTER_INCL_POSTER
- self.incl_subject = plexpy.CONFIG.TWITTER_INCL_SUBJECT
-
- def notify(self, subject, message, **kwargs):
- if not subject or not message:
- return
-
- poster_url = ''
- if self.incl_poster and 'metadata' in kwargs:
- metadata = kwargs['metadata']
- poster_url = metadata.get('poster_url','')
-
- if self.incl_subject:
- return self._send_tweet(subject + '\r\n' + message, attachment=poster_url)
- else:
- return self._send_tweet(message, attachment=poster_url)
-
- def test_notify(self):
- return self._send_tweet("This is a test notification from PlexPy at " + helpers.now())
-
- def _send_tweet(self, message=None, attachment=None):
- consumer_key = self.consumer_key
- consumer_secret = self.consumer_secret
- access_token = self.access_token
- access_token_secret = self.access_token_secret
-
- # logger.info(u"PlexPy Notifiers :: Sending tweet: " + message)
-
- api = twitter.Api(consumer_key, consumer_secret, access_token, access_token_secret)
-
- try:
- api.PostUpdate(message, media=attachment)
- logger.info(u"PlexPy Notifiers :: Twitter notification sent.")
- return True
- except Exception as e:
- logger.warn(u"PlexPy Notifiers :: Twitter notification failed: %s" % e)
- return False
-
- def return_config_options(self):
- config_option = [{'label': 'Instructions',
- 'description': 'Step 1: Visit
\
- Twitter Apps to
Create New App. A vaild "Website" is not required.
\
- Step 2: Go to
Keys and Access Tokens and click \
-
Create my access token.
\
- Step 3: Fill in the
Consumer Key,
Consumer Secret, \
-
Access Token, and
Access Token Secret below.',
- 'input_type': 'help'
- },
- {'label': 'Twitter Consumer Key',
- 'value': self.consumer_key,
- 'name': 'twitter_consumer_key',
- 'description': 'Your Twitter consumer key.',
- 'input_type': 'text'
- },
- {'label': 'Twitter Consumer Secret',
- 'value': self.consumer_secret,
- 'name': 'twitter_consumer_secret',
- 'description': 'Your Twitter consumer secret.',
- 'input_type': 'text'
- },
- {'label': 'Twitter Access Token',
- 'value': self.access_token,
- 'name': 'twitter_access_token',
- 'description': 'Your Twitter access token.',
- 'input_type': 'text'
- },
- {'label': 'Twitter Access Token Secret',
- 'value': self.access_token_secret,
- 'name': 'twitter_access_token_secret',
- 'description': 'Your Twitter access token secret.',
- 'input_type': 'text'
- },
- {'label': 'Include Poster Image',
- 'value': self.incl_poster,
- 'name': 'twitter_incl_poster',
- 'description': 'Include a poster with the notifications.',
- 'input_type': 'checkbox'
- },
- {'label': 'Include Subject Line',
- 'value': self.incl_subject,
- 'name': 'twitter_incl_subject',
- 'description': 'Include the subject line with the notifications.',
- 'input_type': 'checkbox'
- }
- ]
-
- return config_option
-
-
-class OSX_NOTIFY(object):
-
- def __init__(self):
- self.app = plexpy.CONFIG.OSX_NOTIFY_APP
-
- try:
- self.objc = __import__("objc")
- self.AppKit = __import__("AppKit")
- except:
- # logger.error(u"PlexPy Notifiers :: Cannot load OSX Notifications agent.")
- pass
-
- def validate(self):
- try:
- self.objc = __import__("objc")
- self.AppKit = __import__("AppKit")
- return True
- except:
- return False
-
- def swizzle(self, cls, SEL, func):
- old_IMP = cls.instanceMethodForSelector_(SEL)
-
- def wrapper(self, *args, **kwargs):
- return func(self, old_IMP, *args, **kwargs)
- new_IMP = self.objc.selector(wrapper, selector=old_IMP.selector,
- signature=old_IMP.signature)
- self.objc.classAddMethod(cls, SEL, new_IMP)
-
- def notify(self, title, subtitle=None, text=None, sound=True, image=None):
-
- try:
- self.swizzle(self.objc.lookUpClass('NSBundle'),
- b'bundleIdentifier',
- self.swizzled_bundleIdentifier)
-
- NSUserNotification = self.objc.lookUpClass('NSUserNotification')
- NSUserNotificationCenter = self.objc.lookUpClass('NSUserNotificationCenter')
- NSAutoreleasePool = self.objc.lookUpClass('NSAutoreleasePool')
-
- if not NSUserNotification or not NSUserNotificationCenter:
- return False
-
- pool = NSAutoreleasePool.alloc().init()
-
- notification = NSUserNotification.alloc().init()
- notification.setTitle_(title)
- if subtitle:
- notification.setSubtitle_(subtitle)
- if text:
- notification.setInformativeText_(text)
- if sound:
- notification.setSoundName_("NSUserNotificationDefaultSoundName")
- if image:
- source_img = self.AppKit.NSImage.alloc().initByReferencingFile_(image)
- notification.setContentImage_(source_img)
- # notification.set_identityImage_(source_img)
- notification.setHasActionButton_(False)
-
- notification_center = NSUserNotificationCenter.defaultUserNotificationCenter()
- notification_center.deliverNotification_(notification)
- logger.info(u"PlexPy Notifiers :: OSX Notify notification sent.")
-
- del pool
- return True
-
- except Exception as e:
- logger.warn(u"PlexPy Notifiers :: OSX notification failed: %s" % e)
- return False
-
- def swizzled_bundleIdentifier(self, original, swizzled):
- return 'ade.plexpy.osxnotify'
-
- def return_config_options(self):
- config_option = [{'label': 'Register Notify App',
- 'value': self.app,
- 'name': 'osx_notify_app',
- 'description': 'Enter the path/application name to be registered with the '
- 'Notification Center, default is /Applications/PlexPy.',
- 'input_type': 'text'
- }
- ]
-
- return config_option
-
-
-class BOXCAR(object):
-
- def __init__(self):
- self.url = 'https://new.boxcar.io/api/notifications'
- self.token = plexpy.CONFIG.BOXCAR_TOKEN
- self.sound = plexpy.CONFIG.BOXCAR_SOUND
-
- def notify(self, title, message):
- if not title or not message:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
try:
data = urllib.urlencode({
- 'user_credentials': self.token,
- 'notification[title]': title.encode('utf-8'),
- 'notification[long_message]': message.encode('utf-8'),
- 'notification[sound]': self.sound
+ 'user_credentials': self.config['token'],
+ 'notification[title]': subject.encode('utf-8'),
+ 'notification[long_message]': body.encode('utf-8'),
+ 'notification[sound]': self.config['sound']
})
- req = urllib2.Request(self.url)
+ req = urllib2.Request('https://new.boxcar.io/api/notifications')
handle = urllib2.urlopen(req, data)
handle.close()
logger.info(u"PlexPy Notifiers :: Boxcar2 notification sent.")
@@ -1654,14 +1284,14 @@ class BOXCAR(object):
def return_config_options(self):
config_option = [{'label': 'Boxcar Access Token',
- 'value': self.token,
- 'name': 'boxcar_token',
+ 'value': self.config['token'],
+ 'name': 'boxcar2_token',
'description': 'Your Boxcar access token.',
'input_type': 'text'
},
{'label': 'Sound',
- 'value': self.sound,
- 'name': 'boxcar_sound',
+ 'value': self.config['sound'],
+ 'name': 'boxcar2_sound',
'description': 'Set the notification sound. Leave blank for the default sound.',
'input_type': 'select',
'select_options': self.get_sounds()
@@ -1671,55 +1301,56 @@ class BOXCAR(object):
return config_option
-class Email(object):
+class EMAIL(Notifier):
+ """
+ Email notifications
+ """
+ _DEFAULT_CONFIG = {'from_name': '',
+ 'from': '',
+ 'to': '',
+ 'cc': '',
+ 'bcc': '',
+ 'smtp_server': '',
+ 'smtp_port': 25,
+ 'smtp_user': '',
+ 'smtp_password': '',
+ 'tls': 0,
+ 'html_support': 1
+ }
- def __init__(self):
- self.from_name = plexpy.CONFIG.EMAIL_FROM_NAME
- self.email_from = plexpy.CONFIG.EMAIL_FROM
- self.email_to = plexpy.CONFIG.EMAIL_TO
- self.email_cc = plexpy.CONFIG.EMAIL_CC
- self.email_bcc = plexpy.CONFIG.EMAIL_BCC
- self.smtp_server = plexpy.CONFIG.EMAIL_SMTP_SERVER
- self.smtp_port = plexpy.CONFIG.EMAIL_SMTP_PORT
- self.smtp_user = plexpy.CONFIG.EMAIL_SMTP_USER
- self.smtp_password = plexpy.CONFIG.EMAIL_SMTP_PASSWORD
- self.tls = plexpy.CONFIG.EMAIL_TLS
- self.html_support = plexpy.CONFIG.EMAIL_HTML_SUPPORT
-
- def notify(self, subject, message):
- if not subject or not message:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
- if self.html_support:
+ if self.config['html_support']:
msg = MIMEMultipart('alternative')
- msg.attach(MIMEText(bleach.clean(message, strip=True), 'plain', 'utf-8'))
- msg.attach(MIMEText(message, 'html', 'utf-8'))
+ msg.attach(MIMEText(bleach.clean(body, strip=True), 'plain', 'utf-8'))
+ msg.attach(MIMEText(body, 'html', 'utf-8'))
else:
- msg = MIMEText(message, 'plain', 'utf-8')
+ msg = MIMEText(body, 'plain', 'utf-8')
msg['Subject'] = subject
- msg['From'] = email.utils.formataddr((self.from_name, self.email_from))
- msg['To'] = self.email_to
- msg['CC'] = self.email_cc
+ msg['From'] = email.utils.formataddr((self.config['from_name'], self.config['from']))
+ msg['To'] = self.config['to']
+ msg['CC'] = self.config['cc']
-
- recipients = [x.strip() for x in self.email_to.split(';')] \
- + [x.strip() for x in self.email_cc.split(';')] \
- + [x.strip() for x in self.email_bcc.split(';')]
+ recipients = [x.strip() for x in self.config['to'].split(';')] \
+ + [x.strip() for x in self.config['cc'].split(';')] \
+ + [x.strip() for x in self.config['bcc'].split(';')]
recipients = filter(None, recipients)
try:
- mailserver = smtplib.SMTP(self.smtp_server, self.smtp_port)
+ mailserver = smtplib.SMTP(self.config['smtp_server'], self.config['smtp_port'])
- if self.tls:
+ if self.config['tls']:
mailserver.starttls()
mailserver.ehlo()
- if self.smtp_user:
- mailserver.login(self.smtp_user, self.smtp_password)
+ if self.config['smtp_user']:
+ mailserver.login(self.config['smtp_user'], self.config['smtp_password'])
- mailserver.sendmail(self.email_from, recipients, msg.as_string())
+ mailserver.sendmail(self.config['from'], recipients, msg.as_string())
mailserver.quit()
logger.info(u"PlexPy Notifiers :: Email notification sent.")
@@ -1731,67 +1362,67 @@ class Email(object):
def return_config_options(self):
config_option = [{'label': 'From Name',
- 'value': self.from_name,
+ 'value': self.config['from_name'],
'name': 'email_from_name',
'description': 'The name of the sender.',
'input_type': 'text'
},
{'label': 'From',
- 'value': self.email_from,
+ 'value': self.config['from'],
'name': 'email_from',
'description': 'The email address of the sender.',
'input_type': 'text'
},
{'label': 'To',
- 'value': self.email_to,
+ 'value': self.config['to'],
'name': 'email_to',
'description': 'The email address(es) of the recipients, separated by semicolons (;).',
'input_type': 'text'
},
{'label': 'CC',
- 'value': self.email_cc,
+ 'value': self.config['cc'],
'name': 'email_cc',
'description': 'The email address(es) to CC, separated by semicolons (;).',
'input_type': 'text'
},
{'label': 'BCC',
- 'value': self.email_bcc,
+ 'value': self.config['bcc'],
'name': 'email_bcc',
'description': 'The email address(es) to BCC, separated by semicolons (;).',
'input_type': 'text'
},
{'label': 'SMTP Server',
- 'value': self.smtp_server,
+ 'value': self.config['smtp_server'],
'name': 'email_smtp_server',
'description': 'Host for the SMTP server.',
'input_type': 'text'
},
{'label': 'SMTP Port',
- 'value': self.smtp_port,
+ 'value': self.config['smtp_port'],
'name': 'email_smtp_port',
'description': 'Port for the SMTP server.',
'input_type': 'number'
},
{'label': 'SMTP User',
- 'value': self.smtp_user,
+ 'value': self.config['smtp_user'],
'name': 'email_smtp_user',
'description': 'User for the SMTP server.',
'input_type': 'text'
},
{'label': 'SMTP Password',
- 'value': self.smtp_password,
+ 'value': self.config['smtp_password'],
'name': 'email_smtp_password',
'description': 'Password for the SMTP server.',
'input_type': 'password'
},
{'label': 'TLS',
- 'value': self.tls,
+ 'value': self.config['tls'],
'name': 'email_tls',
'description': 'Does the server use encryption.',
'input_type': 'checkbox'
},
{'label': 'Enable HTML Support',
- 'value': self.html_support,
+ 'value': self.config['html_support'],
'name': 'email_html_support',
'description': 'Style your messages using HTML tags.',
'input_type': 'checkbox'
@@ -1800,33 +1431,130 @@ class Email(object):
return config_option
-class IFTTT(object):
- def __init__(self):
- self.apikey = plexpy.CONFIG.IFTTT_KEY
- self.event = plexpy.CONFIG.IFTTT_EVENT
+class TWITTER(Notifier):
+ """
+ Twitter notifications
+ """
+ REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token'
+ ACCESS_TOKEN_URL = 'https://api.twitter.com/oauth/access_token'
+ AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authorize'
+ SIGNIN_URL = 'https://api.twitter.com/oauth/authenticate'
+ _DEFAULT_CONFIG = {'access_token': '',
+ 'access_token_secret': '',
+ 'consumer_key': '',
+ 'consumer_secret': '',
+ 'incl_poster': 0,
+ 'incl_subject': 1
+ }
- def notify(self, message, subject, action):
- if not message or not subject:
+ def _send_tweet(self, message=None, attachment=None):
+ consumer_key = self.config['consumer_key']
+ consumer_secret = self.config['consumer_secret']
+ access_token = self.config['access_token']
+ access_token_secret = self.config['access_token_secret']
+
+ # logger.info(u"PlexPy Notifiers :: Sending tweet: " + message)
+
+ api = twitter.Api(consumer_key, consumer_secret, access_token, access_token_secret)
+
+ try:
+ api.PostUpdate(message, media=attachment)
+ logger.info(u"PlexPy Notifiers :: Twitter notification sent.")
+ return True
+ except Exception as e:
+ logger.warn(u"PlexPy Notifiers :: Twitter notification failed: %s" % e)
+ return False
+
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
- event = unicode(self.event).format(action=action)
- http_handler = HTTPSConnection("maker.ifttt.com")
+ poster_url = ''
+ if self.config['incl_poster'] and 'metadata' in kwargs:
+ metadata = kwargs['metadata']
+ poster_url = metadata.get('poster_url','')
+
+ if self.config['incl_subject']:
+ return self._send_tweet(subject + '\r\n' + body, attachment=poster_url)
+ else:
+ return self._send_tweet(body, attachment=poster_url)
+
+ def return_config_options(self):
+ config_option = [{'label': 'Instructions',
+ 'description': 'Step 1: Visit
\
+ Twitter Apps to
Create New App. A vaild "Website" is not required.
\
+ Step 2: Go to
Keys and Access Tokens and click \
+
Create my access token.
\
+ Step 3: Fill in the
Consumer Key,
Consumer Secret, \
+
Access Token, and
Access Token Secret below.',
+ 'input_type': 'help'
+ },
+ {'label': 'Twitter Consumer Key',
+ 'value': self.config['consumer_key'],
+ 'name': 'twitter_consumer_key',
+ 'description': 'Your Twitter consumer key.',
+ 'input_type': 'text'
+ },
+ {'label': 'Twitter Consumer Secret',
+ 'value': self.config['consumer_secret'],
+ 'name': 'twitter_consumer_secret',
+ 'description': 'Your Twitter consumer secret.',
+ 'input_type': 'text'
+ },
+ {'label': 'Twitter Access Token',
+ 'value': self.config['access_token'],
+ 'name': 'twitter_access_token',
+ 'description': 'Your Twitter access token.',
+ 'input_type': 'text'
+ },
+ {'label': 'Twitter Access Token Secret',
+ 'value': self.config['access_token_secret'],
+ 'name': 'twitter_access_token_secret',
+ 'description': 'Your Twitter access token secret.',
+ 'input_type': 'text'
+ },
+ {'label': 'Include Poster Image',
+ 'value': self.config['incl_poster'],
+ 'name': 'twitter_incl_poster',
+ 'description': 'Include a poster with the notifications.',
+ 'input_type': 'checkbox'
+ },
+ {'label': 'Include Subject Line',
+ 'value': self.config['incl_subject'],
+ 'name': 'twitter_incl_subject',
+ 'description': 'Include the subject line with the notifications.',
+ 'input_type': 'checkbox'
+ }
+ ]
+
+ return config_option
+
+
+class IFTTT(Notifier):
+ """
+ IFTTT notifications
+ """
+ _DEFAULT_CONFIG = {'apikey': '',
+ 'event': 'plexpy'
+ }
+
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
+ return
+
+ event = unicode(self.config['event']).format(action=action)
data = {'value1': subject.encode("utf-8"),
- 'value2': message.encode("utf-8")}
-
- # logger.debug(u"Ifttt SENDING: %s" % json.dumps(data))
+ 'value2': body.encode("utf-8")}
+ http_handler = HTTPSConnection("maker.ifttt.com")
http_handler.request("POST",
- "/trigger/%s/with/key/%s" % (event, self.apikey),
+ "/trigger/%s/with/key/%s" % (event, self.config['apikey']),
headers={'Content-type': "application/json"},
body=json.dumps(data))
response = http_handler.getresponse()
request_status = response.status
- # logger.debug(u"Ifttt response status: %r" % request_status)
- # logger.debug(u"Ifttt response headers: %r" % response.getheaders())
- # logger.debug(u"Ifttt response body: %r" % response.read())
if request_status == 200:
logger.info(u"PlexPy Notifiers :: Ifttt notification sent.")
@@ -1838,19 +1566,16 @@ class IFTTT(object):
logger.warn(u"PlexPy Notifiers :: Ifttt notification failed.")
return False
- def test(self):
- return self.notify('PlexPy', 'Test Message')
-
def return_config_options(self):
config_option = [{'label': 'Ifttt Maker Channel Key',
- 'value': self.apikey,
- 'name': 'ifttt_key',
+ 'value': self.config['apikey'],
+ 'name': 'ifttt_apikey',
'description': 'Your Ifttt key. You can get a key from'
'
here.',
'input_type': 'text'
},
{'label': 'Ifttt Event',
- 'value': self.event,
+ 'value': self.config['event'],
'name': 'ifttt_event',
'description': 'The Ifttt maker event to fire. You can include'
' the {action} to be substituted with the action name.'
@@ -1864,33 +1589,31 @@ class IFTTT(object):
return config_option
-class TELEGRAM(object):
+class TELEGRAM(Notifier):
+ """
+ Telegram notifications
+ """
+ _DEFAULT_CONFIG = {'bot_token': '',
+ 'chat_id': '',
+ 'disable_web_preview': 0,
+ 'html_support': 1,
+ 'incl_poster': 0,
+ 'incl_subject': 1
+ }
- def __init__(self):
- self.enabled = plexpy.CONFIG.TELEGRAM_ENABLED
- self.bot_token = plexpy.CONFIG.TELEGRAM_BOT_TOKEN
- self.chat_id = plexpy.CONFIG.TELEGRAM_CHAT_ID
- self.disable_web_preview = plexpy.CONFIG.TELEGRAM_DISABLE_WEB_PREVIEW
- self.html_support = plexpy.CONFIG.TELEGRAM_HTML_SUPPORT
- self.incl_poster = plexpy.CONFIG.TELEGRAM_INCL_POSTER
- self.incl_subject = plexpy.CONFIG.TELEGRAM_INCL_SUBJECT
-
- def conf(self, options):
- return cherrypy.config['config'].get('Telegram', options)
-
- def notify(self, message, event, **kwargs):
- if not message or not event:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not body or not subject:
return
- data = {'chat_id': self.chat_id}
+ data = {'chat_id': self.config['chat_id']}
- if self.incl_subject:
- text = event.encode('utf-8') + '\r\n' + message.encode('utf-8')
+ if self.config['incl_subject']:
+ text = subject.encode('utf-8') + '\r\n' + body.encode('utf-8')
else:
- text = message.encode('utf-8')
+ text = body.encode('utf-8')
- if self.incl_poster and 'metadata' in kwargs:
- poster_data = {'chat_id': self.chat_id,
+ if self.config['incl_poster'] and 'metadata' in kwargs:
+ poster_data = {'chat_id': self.config['chat_id'],
'disable_notification': True}
metadata = kwargs['metadata']
@@ -1898,7 +1621,7 @@ class TELEGRAM(object):
if poster_url:
files = {'photo': (poster_url, urllib.urlopen(poster_url).read())}
- response = requests.post('https://api.telegram.org/bot%s/%s' % (self.bot_token, 'sendPhoto'),
+ response = requests.post('https://api.telegram.org/bot%s/%s' % (self.config['bot_token'], 'sendPhoto'),
data=poster_data,
files=files)
request_status = response.status_code
@@ -1913,15 +1636,15 @@ class TELEGRAM(object):
data['text'] = text
- if self.html_support:
+ if self.config['html_support']:
data['parse_mode'] = 'HTML'
- if self.disable_web_preview:
+ if self.config['disable_web_preview']:
data['disable_web_page_preview'] = True
http_handler = HTTPSConnection("api.telegram.org")
http_handler.request('POST',
- '/bot%s/%s' % (self.bot_token, 'sendMessage'),
+ '/bot%s/%s' % (self.config['bot_token'], 'sendMessage'),
headers={'Content-type': 'application/x-www-form-urlencoded'},
body=urlencode(data))
@@ -1938,20 +1661,9 @@ class TELEGRAM(object):
logger.warn(u"PlexPy Notifiers :: Telegram notification failed.")
return False
- def updateLibrary(self):
- # For uniformity reasons not removed
- return
-
- def test(self, bot_token, chat_id):
- self.enabled = True
- self.bot_token = bot_token
- self.chat_id = chat_id
-
- self.notify('Main Screen Activate', 'Test Message')
-
def return_config_options(self):
config_option = [{'label': 'Telegram Bot Token',
- 'value': self.bot_token,
+ 'value': self.config['bot_token'],
'name': 'telegram_bot_token',
'description': 'Your Telegram bot token. '
'Contact
@BotFather'
@@ -1959,7 +1671,7 @@ class TELEGRAM(object):
'input_type': 'text'
},
{'label': 'Telegram Chat ID, Group ID, or Channel Username',
- 'value': self.chat_id,
+ 'value': self.config['chat_id'],
'name': 'telegram_chat_id',
'description': 'Your Telegram Chat ID, Group ID, or @channelusername. '
'Contact
@myidbot'
@@ -1967,25 +1679,25 @@ class TELEGRAM(object):
'input_type': 'text'
},
{'label': 'Include Poster Image',
- 'value': self.incl_poster,
+ 'value': self.config['incl_poster'],
'name': 'telegram_incl_poster',
'description': 'Include a poster with the notifications.',
'input_type': 'checkbox'
},
{'label': 'Include Subject Line',
- 'value': self.incl_subject,
+ 'value': self.config['incl_subject'],
'name': 'telegram_incl_subject',
'description': 'Include the subject line with the notifications.',
'input_type': 'checkbox'
},
{'label': 'Enable HTML Support',
- 'value': self.html_support,
+ 'value': self.config['html_support'],
'name': 'telegram_html_support',
'description': 'Style your messages using these HTML tags: b, i, a[href], code, pre.',
'input_type': 'checkbox'
},
{'label': 'Disable Web Page Previews',
- 'value': self.disable_web_preview,
+ 'value': self.config['disable_web_preview'],
'name': 'telegram_disable_web_preview',
'description': 'Disables automatic link previews for links in the message',
'input_type': 'checkbox'
@@ -1995,42 +1707,40 @@ class TELEGRAM(object):
return config_option
-class SLACK(object):
+class SLACK(Notifier):
"""
Slack Notifications
"""
- def __init__(self):
- self.enabled = plexpy.CONFIG.SLACK_ENABLED
- self.slack_hook = plexpy.CONFIG.SLACK_HOOK
- self.channel = plexpy.CONFIG.SLACK_CHANNEL
- self.username = plexpy.CONFIG.SLACK_USERNAME
- self.icon_emoji = plexpy.CONFIG.SLACK_ICON_EMOJI
- self.incl_pmslink = plexpy.CONFIG.SLACK_INCL_PMSLINK
- self.incl_poster = plexpy.CONFIG.SLACK_INCL_POSTER
- self.incl_subject = plexpy.CONFIG.SLACK_INCL_SUBJECT
+ _DEFAULT_CONFIG = {'hook': '',
+ 'channel': '',
+ 'username': '',
+ 'icon_emoji': '',
+ 'incl_pmslink': 0,
+ 'incl_poster': 0,
+ 'incl_subject': 1
+ }
- def conf(self, options):
- return cherrypy.config['config'].get('Slack', options)
-
- def notify(self, message, event, **kwargs):
- if not message or not event:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
- if self.incl_subject:
- text = event.encode('utf-8') + '\r\n' + message.encode("utf-8")
+ if self.config['incl_subject']:
+ text = subject.encode('utf-8') + '\r\n' + body.encode("utf-8")
else:
- text = message.encode("utf-8")
+ text = body.encode("utf-8")
data = {'text': text}
- if self.channel != '': data['channel'] = self.channel
- if self.username != '': data['username'] = self.username
- if self.icon_emoji != '':
- if urlparse(self.icon_emoji).scheme == '':
- data['icon_emoji'] = self.icon_emoji
+ if self.config['channel'] != '':
+ data['channel'] = self.config['channel']
+ if self.config['username'] != '':
+ data['username'] = self.config['username']
+ if self.config['icon_emoji'] != '':
+ if urlparse(self.config['icon_emoji']).scheme == '':
+ data['icon_emoji'] = self.config['icon_emoji']
else:
- data['icon_url'] = self.icon_emoji
+ data['icon_url'] = self.config['icon_emoji']
- if self.incl_poster and 'metadata' in kwargs:
+ if self.config['incl_poster'] and 'metadata' in kwargs:
# Grab formatted metadata
pretty_metadata = PrettyMetadata(kwargs['metadata'])
poster_url = pretty_metadata.get_poster_url()
@@ -2053,7 +1763,7 @@ class SLACK(object):
attachment['title_link'] = poster_link
fields.append({'value': '<%s|%s>' % (poster_link, caption),
'short': True})
- if self.incl_pmslink:
+ if self.config['incl_pmslink']:
fields.append({'value': '<%s|%s>' % (plex_url, 'View on Plex Web'),
'short': True})
if fields:
@@ -2061,8 +1771,8 @@ class SLACK(object):
data['attachments'] = [attachment]
- slackhost = urlparse(self.slack_hook).hostname
- slackpath = urlparse(self.slack_hook).path
+ slackhost = urlparse(self.config['hook']).hostname
+ slackpath = urlparse(self.config['hook']).path
http_handler = HTTPSConnection(slackhost)
http_handler.request("POST",
@@ -2083,54 +1793,46 @@ class SLACK(object):
logger.warn(u"PlexPy Notifiers :: Slack notification failed.")
return False
- def updateLibrary(self):
- #For uniformity reasons not removed
- return
-
- def test(self):
- self.enabled = True
- return self.notify('Main Screen Activate', 'Test Message')
-
def return_config_options(self):
config_option = [{'label': 'Slack Webhook URL',
- 'value': self.slack_hook,
+ 'value': self.config['hook'],
'name': 'slack_hook',
'description': 'Your Slack incoming webhook URL.',
'input_type': 'text'
},
{'label': 'Slack Channel',
- 'value': self.channel,
+ 'value': self.config['channel'],
'name': 'slack_channel',
'description': 'Your Slack channel name (begin with \'#\'). Leave blank for webhook integration default.',
'input_type': 'text'
},
{'label': 'Slack Username',
- 'value': self.username,
+ 'value': self.config['username'],
'name': 'slack_username',
'description': 'The Slack username which will be shown. Leave blank for webhook integration default.',
'input_type': 'text'
},
{'label': 'Slack Icon',
- 'value': self.icon_emoji,
+ 'value': self.config['icon_emoji'],
'description': 'The icon you wish to show, use Slack emoji or image url. Leave blank for webhook integration default.',
'name': 'slack_icon_emoji',
'input_type': 'text'
},
{'label': 'Include Poster Image',
- 'value': self.incl_poster,
+ 'value': self.config['incl_poster'],
'name': 'slack_incl_poster',
'description': 'Include a poster with the notifications.',
'input_type': 'checkbox'
},
{'label': 'Include Link to Plex Web',
- 'value': self.incl_pmslink,
+ 'value': self.config['incl_pmslink'],
'name': 'slack_incl_pmslink',
'description': 'Include a link to the media in Plex Web with the notifications.
'
'If disabled, the link will go to IMDB, TVDB, TMDb, or Last.fm instead, if available.',
'input_type': 'checkbox'
},
{'label': 'Include Subject Line',
- 'value': self.incl_subject,
+ 'value': self.config['incl_subject'],
'name': 'slack_incl_subject',
'description': 'Include the subject line with the notifications.',
'input_type': 'checkbox'
@@ -2140,41 +1842,31 @@ class SLACK(object):
return config_option
-class Scripts(object):
+class SCRIPTS(Notifier):
+ """
+ Script notifications
+ """
+ _DEFAULT_CONFIG = {'script_folder': '',
+ 'script': '',
+ 'timeout': 30
+ }
- def __init__(self, **kwargs):
- self.script_exts = ('.bat', '.cmd', '.exe', '.php', '.pl', '.ps1', '.py', '.pyw', '.rb', '.sh')
- self.script_folder = plexpy.CONFIG.SCRIPTS_FOLDER
- self.script_timeout = plexpy.CONFIG.SCRIPTS_TIMEOUT
- self.scripts = {'play': plexpy.CONFIG.SCRIPTS_ON_PLAY_SCRIPT,
- 'stop': plexpy.CONFIG.SCRIPTS_ON_STOP_SCRIPT,
- 'pause': plexpy.CONFIG.SCRIPTS_ON_PAUSE_SCRIPT,
- 'resume': plexpy.CONFIG.SCRIPTS_ON_RESUME_SCRIPT,
- 'watched': plexpy.CONFIG.SCRIPTS_ON_WATCHED_SCRIPT,
- 'buffer': plexpy.CONFIG.SCRIPTS_ON_BUFFER_SCRIPT,
- 'created': plexpy.CONFIG.SCRIPTS_ON_CREATED_SCRIPT,
- 'intdown': plexpy.CONFIG.SCRIPTS_ON_INTDOWN_SCRIPT,
- 'intup': plexpy.CONFIG.SCRIPTS_ON_INTUP_SCRIPT,
- 'extdown': plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT,
- 'extup': plexpy.CONFIG.SCRIPTS_ON_EXTUP_SCRIPT,
- 'pmsupdate': plexpy.CONFIG.SCRIPTS_ON_PMSUPDATE_SCRIPT,
- 'concurrent': plexpy.CONFIG.SCRIPTS_ON_CONCURRENT_SCRIPT,
- 'newdevice': plexpy.CONFIG.SCRIPTS_ON_NEWDEVICE_SCRIPT
- }
-
- def conf(self, options):
- return cherrypy.config['config'].get('Scripts', options)
-
- def updateLibrary(self):
- # For uniformity reasons not removed
- return
-
- def test(self, subject, message, *args, **kwargs):
- self.notify(subject, message, *args, **kwargs)
- return
+ def __init__(self, config=None):
+ self.set_config(config)
+ self.script_exts = {'.bat': '',
+ '.cmd': '',
+ '.exe': '',
+ '.php': 'php',
+ '.pl': 'perl',
+ '.ps1': 'powershell -executionPolicy bypass -file',
+ '.py': 'python',
+ '.pyw': 'pythonw',
+ '.rb': 'ruby',
+ '.sh': ''
+ }
def list_scripts(self):
- scriptdir = self.script_folder
+ scriptdir = self.config['script_folder']
scripts = {'': ''}
if scriptdir and not os.path.exists(scriptdir):
@@ -2183,7 +1875,7 @@ class Scripts(object):
for root, dirs, files in os.walk(scriptdir):
for f in files:
name, ext = os.path.splitext(f)
- if ext in self.script_exts:
+ if ext in self.script_exts.keys():
rfp = os.path.join(os.path.relpath(root, scriptdir), f)
fp = os.path.join(root, f)
scripts[fp] = rfp
@@ -2193,21 +1885,20 @@ class Scripts(object):
def run_script(self, script):
def kill_script(process):
logger.warn(u"PlexPy Notifiers :: Script exceeded timeout limit of %d seconds. "
- "Script killed." % self.script_timeout)
+ "Script killed." % self.config['timeout'])
process.kill()
self.script_killed = True
self.script_killed = False
output = error = ''
try:
-
process = subprocess.Popen(script,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
- cwd=self.script_folder)
+ cwd=self.config['script_folder'])
- if self.script_timeout:
+ if self.config['timeout']:
timer = threading.Timer(self.script_timeout, kill_script, (process,))
else:
timer = None
@@ -2236,49 +1927,37 @@ class Scripts(object):
logger.info(u"PlexPy Notifiers :: Script notification sent.")
return True
- def notify(self, subject='', message='', notify_action='', script_args=None, *args, **kwargs):
+ def notify(self, subject='', body='', action='', **kwargs):
"""
Args:
- subject(string, optional): Head text,
- message(string, optional): Body text,
+ subject(string, optional): Subject text,
+ body(string, optional): Body text,
notify_action(string): 'play'
- script_args(list): ["python2", '-p', '-zomg']
"""
- logger.debug(u"PlexPy Notifiers :: Trying to run notify script, action: %s, arguments: %s" %
- (notify_action or None, script_args or None))
+ script_args = subject or None
+ action = kwargs.get('action', None)
- if script_args is None:
+ logger.debug(u"PlexPy Notifiers :: Trying to run notify script, action: %s, arguments: %s"
+ % (action, script_args))
+
+ if not script_args:
script_args = []
- if not self.script_folder:
+ if not self.config['script_folder']:
return
- script = self.scripts.get(notify_action, kwargs.get('script', ''))
+ script = self.config.get('script', kwargs.get('script', ''))
# Don't try to run the script if the action does not have one
- if notify_action and not script:
- logger.debug(u"PlexPy Notifiers :: No script selected for action %s, exiting..." % notify_action)
+ if action and not script:
+ logger.debug(u"PlexPy Notifiers :: No script selected for action %s, exiting..." % action)
return
elif not script:
logger.debug(u"PlexPy Notifiers :: No script selected, exiting...")
return
name, ext = os.path.splitext(script)
-
- if ext == '.php':
- prefix = 'php'
- elif ext == '.pl':
- prefix = 'perl'
- elif ext == '.ps1':
- prefix = 'powershell -executionPolicy bypass -file'
- elif ext == '.py':
- prefix = 'python'
- elif ext == '.pyw':
- prefix = 'pythonw'
- elif ext == '.rb':
- prefix = 'ruby'
- else:
- prefix = ''
+ prefix = self.script_exts.get(ext, '')
if os.name == 'nt':
script = script.encode(plexpy.SYS_ENCODING, 'ignore')
@@ -2314,143 +1993,194 @@ class Scripts(object):
def return_config_options(self):
config_option = [{'label': 'Supported File Types',
- 'description': ', '.join(self.script_exts),
+ 'description': '
' + \
+ ', '.join(self.script_exts.keys()) + '',
'input_type': 'help'
},
{'label': 'Script Folder',
- 'value': self.script_folder,
- 'name': 'scripts_folder',
- 'description': 'Add your script folder.',
+ 'value': self.config['script_folder'],
+ 'name': 'scripts_script_folder',
+ 'description': 'Set your script folder.',
'input_type': 'text',
+ 'refresh': True
},
- {'label': 'Playback Start',
- 'value': self.scripts['play'],
- 'name': 'scripts_on_play_script',
- 'description': 'Choose the script for on play.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Playback Stop',
- 'value': self.scripts['stop'],
- 'name': 'scripts_on_stop_script',
- 'description': 'Choose the script for on stop.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Playback Pause',
- 'value': self.scripts['pause'],
- 'name': 'scripts_on_pause_script',
- 'description': 'Choose the script for on pause.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Playback Resume',
- 'value': self.scripts['resume'],
- 'name': 'scripts_on_resume_script',
- 'description': 'Choose the script for on resume.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Watched',
- 'value': self.scripts['watched'],
- 'name': 'scripts_on_watched_script',
- 'description': 'Choose the script for on watched.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Buffer Warnings',
- 'value': self.scripts['buffer'],
- 'name': 'scripts_on_buffer_script',
- 'description': 'Choose the script for buffer warnings.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Recently Added',
- 'value': self.scripts['created'],
- 'name': 'scripts_on_created_script',
- 'description': 'Choose the script for recently added.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Plex Server Down',
- 'value': self.scripts['intdown'],
- 'name': 'scripts_on_intdown_script',
- 'description': 'Choose the script for Plex server down.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Plex Server Back Up',
- 'value': self.scripts['intup'],
- 'name': 'scripts_on_intup_script',
- 'description': 'Choose the script for Plex server back up.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Plex Remote Access Down',
- 'value': self.scripts['extdown'],
- 'name': 'scripts_on_extdown_script',
- 'description': 'Choose the script for Plex remote access down.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Plex Remote Access Back Up',
- 'value': self.scripts['extup'],
- 'name': 'scripts_on_extup_script',
- 'description': 'Choose the script for Plex remote access back up.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'Plex Update Available',
- 'value': self.scripts['pmsupdate'],
- 'name': 'scripts_on_pmsupdate_script',
- 'description': 'Choose the script for Plex update available.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'User Concurrent Streams',
- 'value': self.scripts['concurrent'],
- 'name': 'scripts_on_concurrent_script',
- 'description': 'Choose the script for user concurrent streams.',
- 'input_type': 'select',
- 'select_options': self.list_scripts()
- },
- {'label': 'User New Device',
- 'value': self.scripts['newdevice'],
- 'name': 'scripts_on_newdevice_script',
- 'description': 'Choose the script for user new device.',
+ {'label': 'Script File',
+ 'value': self.config['script'],
+ 'name': 'scripts_script',
+ 'description': 'Choose the script file.',
'input_type': 'select',
'select_options': self.list_scripts()
},
{'label': 'Script Timeout',
- 'value': self.script_timeout,
+ 'value': self.config['timeout'],
'name': 'scripts_timeout',
'description': 'The number of seconds to wait before killing the script. 0 to disable timeout.',
'input_type': 'number'
+ # },
+ #{'label': 'Playback Stop',
+ # 'value': self.scripts['stop'],
+ # 'name': 'scripts_on_stop_script',
+ # 'description': 'Choose the script for on stop.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'Playback Pause',
+ # 'value': self.scripts['pause'],
+ # 'name': 'scripts_on_pause_script',
+ # 'description': 'Choose the script for on pause.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'Playback Resume',
+ # 'value': self.scripts['resume'],
+ # 'name': 'scripts_on_resume_script',
+ # 'description': 'Choose the script for on resume.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'Watched',
+ # 'value': self.scripts['watched'],
+ # 'name': 'scripts_on_watched_script',
+ # 'description': 'Choose the script for on watched.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'Buffer Warnings',
+ # 'value': self.scripts['buffer'],
+ # 'name': 'scripts_on_buffer_script',
+ # 'description': 'Choose the script for buffer warnings.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'Recently Added',
+ # 'value': self.scripts['created'],
+ # 'name': 'scripts_on_created_script',
+ # 'description': 'Choose the script for recently added.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'Plex Server Down',
+ # 'value': self.scripts['intdown'],
+ # 'name': 'scripts_on_intdown_script',
+ # 'description': 'Choose the script for Plex server down.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'Plex Server Back Up',
+ # 'value': self.scripts['intup'],
+ # 'name': 'scripts_on_intup_script',
+ # 'description': 'Choose the script for Plex server back up.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'Plex Remote Access Down',
+ # 'value': self.scripts['extdown'],
+ # 'name': 'scripts_on_extdown_script',
+ # 'description': 'Choose the script for Plex remote access down.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'Plex Remote Access Back Up',
+ # 'value': self.scripts['extup'],
+ # 'name': 'scripts_on_extup_script',
+ # 'description': 'Choose the script for Plex remote access back up.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'Plex Update Available',
+ # 'value': self.scripts['pmsupdate'],
+ # 'name': 'scripts_on_pmsupdate_script',
+ # 'description': 'Choose the script for Plex update available.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'User Concurrent Streams',
+ # 'value': self.scripts['concurrent'],
+ # 'name': 'scripts_on_concurrent_script',
+ # 'description': 'Choose the script for user concurrent streams.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
+ # },
+ #{'label': 'User New Device',
+ # 'value': self.scripts['newdevice'],
+ # 'name': 'scripts_on_newdevice_script',
+ # 'description': 'Choose the script for user new device.',
+ # 'input_type': 'select',
+ # 'select_options': self.list_scripts()
}
]
return config_option
-class FacebookNotifier(object):
+class FACEBOOK(Notifier):
+ """
+ Facebook notifications
+ """
+ _DEFAULT_CONFIG = {'redirect_uri': '',
+ 'access_token': '',
+ 'app_id': '',
+ 'app_secret': '',
+ 'group_id': '',
+ 'incl_pmslink': 0,
+ 'incl_poster': 0,
+ 'incl_subject': 1
+ }
- def __init__(self):
- self.redirect_uri = plexpy.CONFIG.FACEBOOK_REDIRECT_URI
- self.access_token = plexpy.CONFIG.FACEBOOK_TOKEN
- self.app_id = plexpy.CONFIG.FACEBOOK_APP_ID
- self.app_secret = plexpy.CONFIG.FACEBOOK_APP_SECRET
- self.group_id = plexpy.CONFIG.FACEBOOK_GROUP
- self.incl_pmslink = plexpy.CONFIG.FACEBOOK_INCL_PMSLINK
- self.incl_poster = plexpy.CONFIG.FACEBOOK_INCL_POSTER
- self.incl_subject = plexpy.CONFIG.FACEBOOK_INCL_SUBJECT
+ def _get_authorization(self):
+ return facebook.auth_url(app_id=self.config['app_id'],
+ canvas_url=self.config['redirect_uri'] + '/facebookStep2',
+ perms=['user_managed_groups','publish_actions'])
- def notify(self, subject, message, **kwargs):
- if not subject or not message:
+ def _get_credentials(self, code):
+ logger.info(u"PlexPy Notifiers :: Requesting access token from Facebook")
+
+ try:
+ # Request user access token
+ api = facebook.GraphAPI(version='2.5')
+ response = api.get_access_token_from_code(code=code,
+ redirect_uri=self.config['redirect_uri'] + '/facebookStep2',
+ app_id=self.config['app_id'],
+ app_secret=self.config['app_secret'])
+ access_token = response['access_token']
+
+ # Request extended user access token
+ api = facebook.GraphAPI(access_token=access_token, version='2.5')
+ response = api.extend_access_token(app_id=self.config['app_id'],
+ app_secret=self.config['app_secret'])
+ access_token = response['access_token']
+
+ plexpy.CONFIG.FACEBOOK_TOKEN = access_token
+ plexpy.CONFIG.write()
+ except Exception as e:
+ logger.error(u"PlexPy Notifiers :: Error requesting Facebook access token: %s" % e)
+ return False
+
+ return True
+
+ def _post_facebook(self, message=None, attachment=None):
+ if self.config['group_id']:
+ api = facebook.GraphAPI(access_token=self.config['access_token'], version='2.5')
+
+ try:
+ api.put_wall_post(profile_id=self.config['group_id'], message=message, attachment=attachment)
+ logger.info(u"PlexPy Notifiers :: Facebook notification sent.")
+ return True
+ except Exception as e:
+ logger.warn(u"PlexPy Notifiers :: Error sending Facebook post: %s" % e)
+ return False
+
+ else:
+ logger.warn(u"PlexPy Notifiers :: Error sending Facebook post: No Facebook Group ID provided.")
+ return False
+
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
attachment = {}
- if self.incl_poster and 'metadata' in kwargs:
+ if self.config['incl_poster'] and 'metadata' in kwargs:
# Grab formatted metadata
pretty_metadata = PrettyMetadata(kwargs['metadata'])
poster_url = pretty_metadata.get_poster_url()
@@ -2461,7 +2191,7 @@ class FacebookNotifier(object):
subtitle = pretty_metadata.get_subtitle()
# Build Facebook post attachment
- if self.incl_pmslink:
+ if self.config['incl_pmslink']:
attachment['link'] = plex_url
attachment['caption'] = 'View on Plex Web'
elif poster_link:
@@ -2474,60 +2204,10 @@ class FacebookNotifier(object):
attachment['name'] = title
attachment['description'] = subtitle
- if self.incl_subject:
- return self._post_facebook(subject + '\r\n' + message, attachment=attachment)
+ if self.config['incl_subject']:
+ return self._post_facebook(subject + '\r\n' + body, attachment=attachment)
else:
- return self._post_facebook(message, attachment=attachment)
-
- def test_notify(self):
- return self._post_facebook(u"PlexPy Notifiers :: This is a test notification from PlexPy at " + helpers.now())
-
- def _get_authorization(self):
- return facebook.auth_url(app_id=self.app_id,
- canvas_url=self.redirect_uri + '/facebookStep2',
- perms=['user_managed_groups','publish_actions'])
-
- def _get_credentials(self, code):
- logger.info(u"PlexPy Notifiers :: Requesting access token from Facebook")
-
- try:
- # Request user access token
- api = facebook.GraphAPI(version='2.5')
- response = api.get_access_token_from_code(code=code,
- redirect_uri=self.redirect_uri + '/facebookStep2',
- app_id=self.app_id,
- app_secret=self.app_secret)
- access_token = response['access_token']
-
- # Request extended user access token
- api = facebook.GraphAPI(access_token=access_token, version='2.5')
- response = api.extend_access_token(app_id=self.app_id,
- app_secret=self.app_secret)
- access_token = response['access_token']
-
- plexpy.CONFIG.FACEBOOK_TOKEN = access_token
- plexpy.CONFIG.write()
- except Exception as e:
- logger.error(u"PlexPy Notifiers :: Error requesting Facebook access token: %s" % e)
- return False
-
- return True
-
- def _post_facebook(self, message=None, attachment=None):
- if self.group_id:
- api = facebook.GraphAPI(access_token=self.access_token, version='2.5')
-
- try:
- api.put_wall_post(profile_id=self.group_id, message=message, attachment=attachment)
- logger.info(u"PlexPy Notifiers :: Facebook notification sent.")
- return True
- except Exception as e:
- logger.warn(u"PlexPy Notifiers :: Error sending Facebook post: %s" % e)
- return False
-
- else:
- logger.warn(u"PlexPy Notifiers :: Error sending Facebook post: No Facebook Group ID provided.")
- return False
+ return self._post_facebook(body, attachment=attachment)
def return_config_options(self):
config_option = [{'label': 'Instructions',
@@ -2544,51 +2224,51 @@ class FacebookNotifier(object):
'input_type': 'help'
},
{'label': 'PlexPy URL',
- 'value': self.redirect_uri,
+ 'value': self.config['redirect_uri'],
'name': 'facebook_redirect_uri',
'description': 'Your PlexPy URL. This will tell Facebook where to redirect you after authorization.\
(e.g. http://localhost:8181)',
'input_type': 'text'
},
{'label': 'Facebook App ID',
- 'value': self.app_id,
+ 'value': self.config['app_id'],
'name': 'facebook_app_id',
'description': 'Your Facebook app ID.',
'input_type': 'text'
},
{'label': 'Facebook App Secret',
- 'value': self.app_secret,
+ 'value': self.config['app_secret'],
'name': 'facebook_app_secret',
'description': 'Your Facebook app secret.',
'input_type': 'text'
},
{'label': 'Request Authorization',
'value': 'Request Authorization',
- 'name': 'facebookStep1',
+ 'name': 'facebook_facebookStep1',
'description': 'Request Facebook authorization. (Ensure you allow the browser pop-up).',
'input_type': 'button'
},
{'label': 'Facebook Group ID',
- 'value': self.group_id,
- 'name': 'facebook_group',
+ 'value': self.config['group_id'],
+ 'name': 'facebook_group_id',
'description': 'Your Facebook Group ID.',
'input_type': 'text'
},
{'label': 'Include Poster Image',
- 'value': self.incl_poster,
+ 'value': self.config['incl_poster'],
'name': 'facebook_incl_poster',
'description': 'Include a poster with the notifications.',
'input_type': 'checkbox'
},
{'label': 'Include Link to Plex Web',
- 'value': self.incl_pmslink,
+ 'value': self.config['incl_pmslink'],
'name': 'facebook_incl_pmslink',
'description': 'Include a link to the media in Plex Web with the notifications.
'
'If disabled, the link will go to IMDB, TVDB, TMDb, or Last.fm instead, if available.',
'input_type': 'checkbox'
},
{'label': 'Include Subject Line',
- 'value': self.incl_subject,
+ 'value': self.config['incl_subject'],
'name': 'facebook_incl_subject',
'description': 'Include the subject line with the notifications.',
'input_type': 'checkbox'
@@ -2598,21 +2278,23 @@ class FacebookNotifier(object):
return config_option
-class Browser(object):
+class BROWSER(Notifier):
+ """
+ Browser notifications
+ """
+ _DEFAULT_CONFIG = {'enabled': 0,
+ 'auto_hide_delay': 5
+ }
- def __init__(self):
- self.enabled = plexpy.CONFIG.BROWSER_ENABLED
- self.auto_hide_delay = plexpy.CONFIG.BROWSER_AUTO_HIDE_DELAY
-
- def notify(self, subject, message):
- if not subject or not message:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
logger.info(u"PlexPy Notifiers :: Browser notification sent.")
return True
def get_notifications(self):
- if not self.enabled:
+ if not self.config['enabled']:
return
monitor_db = database.MonitorDatabase()
@@ -2624,30 +2306,26 @@ class Browser(object):
for item in result:
notification = {'subject_text': item['subject_text'],
'body_text': item['body_text'],
- 'delay': self.auto_hide_delay}
+ 'delay': self.config['auto_hide_delay']}
notifications.append(notification)
return {'notifications': notifications}
- def test(self, bot_token, chat_id):
- self.enabled = True
- self.notify('PlexPy', 'Test Notification')
-
def return_config_options(self):
config_option = [{'label': 'Enable Browser Notifications',
- 'value': self.enabled,
+ 'value': self.config['enabled'],
'name': 'browser_enabled',
'description': 'Enable to display desktop notifications from your browser.',
'input_type': 'checkbox'
},
{'label': 'Allow Notifications',
'value': 'Allow Notifications',
- 'name': 'allow_browser',
+ 'name': 'browser_allow_browser',
'description': 'Click to allow browser notifications. You must click this button for each browser.',
'input_type': 'button'
},
{'label': 'Auto Hide Delay',
- 'value': self.auto_hide_delay,
+ 'value': self.config['auto_hide_delay'],
'name': 'browser_auto_hide_delay',
'description': 'Set the number of seconds for the notification to remain visible. \
Set 0 to disable auto hiding. (Note: Some browsers have a maximum time limit.)',
@@ -2658,27 +2336,26 @@ class Browser(object):
return config_option
-class JOIN(object):
+class JOIN(Notifier):
+ """
+ Join notifications
+ """
+ _DEFAULT_CONFIG = {'apikey': '',
+ 'deviceid': '',
+ 'incl_subject': 1
+ }
- def __init__(self):
- self.apikey = plexpy.CONFIG.JOIN_APIKEY
- self.deviceid = plexpy.CONFIG.JOIN_DEVICEID
- self.incl_subject = plexpy.CONFIG.JOIN_INCL_SUBJECT
-
- def conf(self, options):
- return cherrypy.config['config'].get('PUSHBULLET', options)
-
- def notify(self, message, subject):
- if not message or not subject:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subject or not body:
return
- deviceid_key = 'deviceId%s' % ('s' if len(self.deviceid.split(',')) > 1 else '')
+ deviceid_key = 'deviceId%s' % ('s' if len(self.config['deviceid'].split(',')) > 1 else '')
- data = {'apikey': self.apikey,
- deviceid_key: self.deviceid,
- 'text': message.encode("utf-8")}
+ data = {'apikey': self.config['apikey'],
+ deviceid_key: self.config['deviceid'],
+ 'text': body.encode("utf-8")}
- if self.incl_subject:
+ if self.config['incl_subject']:
data['title'] = subject.encode("utf-8")
response = requests.post('https://joinjoaomgcd.appspot.com/_ah/api/messaging/v1/sendPush',
@@ -2701,19 +2378,11 @@ class JOIN(object):
logger.warn(u"PlexPy Notifiers :: Join notification failed.")
return False
- def test(self, apikey, deviceid):
-
- self.enabled = True
- self.apikey = apikey
- self.deviceid = deviceid
-
- self.notify('Main Screen Activate', 'Test Message')
-
def get_devices(self):
- if self.apikey:
+ if self.config['apikey']:
http_handler = HTTPSConnection("joinjoaomgcd.appspot.com")
http_handler.request("GET",
- "/_ah/api/registration/v1/listDevices?%s" % urlencode({'apikey': self.apikey}))
+ "/_ah/api/registration/v1/listDevices?%s" % urlencode({'apikey': self.config['apikey']}))
response = http_handler.getresponse()
request_status = response.status
@@ -2746,13 +2415,14 @@ class JOIN(object):
devices = 'Enter your Join API key to load your device list.'
config_option = [{'label': 'Join API Key',
- 'value': self.apikey,
+ 'value': self.config['apikey'],
'name': 'join_apikey',
'description': 'Your Join API key. Required for group notifications.',
- 'input_type': 'text'
+ 'input_type': 'text',
+ 'refresh': True
},
{'label': 'Device ID(s) or Group ID',
- 'value': self.deviceid,
+ 'value': self.config['deviceid'],
'name': 'join_deviceid',
'description': 'Set your Join device ID or group ID. ' \
'Separate multiple devices with commas (,).',
@@ -2763,7 +2433,7 @@ class JOIN(object):
'input_type': 'help'
},
{'label': 'Include Subject Line',
- 'value': self.incl_subject,
+ 'value': self.config['incl_subject'],
'name': 'join_incl_subject',
'description': 'Include the subject line with the notifications.',
'input_type': 'checkbox'
@@ -2772,31 +2442,34 @@ class JOIN(object):
return config_option
-class HIPCHAT(object):
- def __init__(self):
- self.apiurl = plexpy.CONFIG.HIPCHAT_URL
- self.color = plexpy.CONFIG.HIPCHAT_COLOR
- self.emoticon = plexpy.CONFIG.HIPCHAT_EMOTICON
- self.incl_pmslink = plexpy.CONFIG.HIPCHAT_INCL_PMSLINK
- self.incl_poster = plexpy.CONFIG.HIPCHAT_INCL_POSTER
- self.incl_subject = plexpy.CONFIG.HIPCHAT_INCL_SUBJECT
+class HIPCHAT(Notifier):
+ """
+ Hipchat notifications
+ """
+ _DEFAULT_CONFIG = {'apiurl': '',
+ 'color': '',
+ 'emoticon': '',
+ 'incl_pmslink': 0,
+ 'incl_poster': 0,
+ 'incl_subject': 1
+ }
- def notify(self, message, subject, **kwargs):
- if not message or not subject:
+ def notify(self, subject='', body='', action='', **kwargs):
+ if not subjecy or not body:
return
data = {'notify': 'false'}
- text = message.encode('utf-8')
+ text = body.encode('utf-8')
- if self.incl_subject:
+ if self.config['incl_subject']:
data['from'] = subject.encode('utf-8')
- if self.color:
- data['color'] = self.color
+ if self.config['color']:
+ data['color'] = self.config['color']
- if self.incl_poster and 'metadata' in kwargs:
+ if self.config['incl_poster'] and 'metadata' in kwargs:
pretty_metadata = PrettyMetadata(kwargs['metadata'])
poster_url = pretty_metadata.get_poster_url()
poster_link = pretty_metadata.get_poster_link()
@@ -2821,7 +2494,7 @@ class HIPCHAT(object):
card['url'] = poster_link
attributes.append({'value': {'label': caption,
'url': poster_link}})
- if self.incl_pmslink:
+ if self.config['incl_pmslink']:
attributes.append({'value': {'label': 'View on Plex Web',
'url': plex_url}})
if attributes:
@@ -2831,13 +2504,13 @@ class HIPCHAT(object):
data['card'] = card
else:
- if self.emoticon:
- text = self.emoticon + ' ' + text
+ if self.config['emoticon']:
+ text = self.config['emoticon'] + ' ' + text
data['message'] = text
data['message_format'] = 'text'
- hiphost = urlparse(self.apiurl).hostname
- hipfullq = urlparse(self.apiurl).path + '?' + urlparse(self.apiurl).query
+ hiphost = urlparse(self.config['apiurl']).hostname
+ hipfullq = urlparse(self.config['apiurl']).path + '?' + urlparse(self.config['apiurl']).query
http_handler = HTTPSConnection(hiphost)
http_handler.request("POST",
@@ -2857,26 +2530,16 @@ class HIPCHAT(object):
logger.warn(u"PlexPy Notifiers :: Hipchat notification failed.")
return False
- def test(self, apiurl, color, hipchat_emoticon, hipchat_incl_subject):
-
- self.enabled = True
- self.apiurl = apiurl
- self.color = color
- self.emoticon = hipchat_emoticon
- self.incl_subject = hipchat_incl_subject
-
- return self.notify('PlexPy', 'Test Message')
-
def return_config_options(self):
config_option = [{'label': 'Hipchat Custom Integrations Full URL',
- 'value': self.apiurl,
- 'name': 'hipchat_url',
+ 'value': self.config['apiurl'],
+ 'name': 'hipchat_apiurl',
'description': 'Your Hipchat BYO integration URL. You can get a key from'
'
here.',
'input_type': 'text'
},
{'label': 'Hipchat Color',
- 'value': self.color,
+ 'value': self.config['color'],
'name': 'hipchat_color',
'description': 'Background color for the message.',
'input_type': 'select',
@@ -2890,7 +2553,7 @@ class HIPCHAT(object):
}
},
{'label': 'Hipchat Emoticon',
- 'value': self.emoticon,
+ 'value': self.config['emoticon'],
'name': 'hipchat_emoticon',
'description': 'Include an emoticon tag at the beginning of text notifications (e.g. (taco)). Leave blank for none.'
' Use a stock emoticon or create a custom emoticon'
@@ -2898,23 +2561,118 @@ class HIPCHAT(object):
'input_type': 'text'
},
{'label': 'Include Poster',
- 'value': self.incl_poster,
+ 'value': self.config['incl_poster'],
'name': 'hipchat_incl_poster',
'description': 'Include a poster in the notifications.
This will change the notification type to HTML and emoticons will no longer work.',
'input_type': 'checkbox'
},
{'label': 'Include Link to Plex Web',
- 'value': self.incl_pmslink,
+ 'value': self.config['incl_pmslink'],
'name': 'hipchat_incl_pmslink',
'description': 'Include a link to the media in Plex Web with the notifications.',
'input_type': 'checkbox'
},
{'label': 'Include Subject Line',
- 'value': self.incl_subject,
+ 'value': self.config['incl_subject'],
'name': 'hipchat_incl_subject',
'description': 'Includes the subject with the notifications.',
'input_type': 'checkbox'
}
]
- return config_option
\ No newline at end of file
+ return config_option
+
+
+class OSX(Notifier):
+ """
+ OSX notifications
+ """
+ _DEFAULT_CONFIG = {'notify_app': '/Applications/PlexPy'
+ }
+
+ def __init__(self, config=None):
+ self.set_config(config)
+
+ try:
+ self.objc = __import__("objc")
+ self.AppKit = __import__("AppKit")
+ except:
+ # logger.error(u"PlexPy Notifiers :: Cannot load OSX Notifications agent.")
+ pass
+
+ def validate(self):
+ try:
+ self.objc = __import__("objc")
+ self.AppKit = __import__("AppKit")
+ return True
+ except:
+ return False
+
+ def _swizzle(self, cls, SEL, func):
+ old_IMP = cls.instanceMethodForSelector_(SEL)
+
+ def wrapper(self, *args, **kwargs):
+ return func(self, old_IMP, *args, **kwargs)
+ new_IMP = self.objc.selector(wrapper, selector=old_IMP.selector,
+ signature=old_IMP.signature)
+ self.objc.classAddMethod(cls, SEL, new_IMP)
+
+ def _swizzled_bundleIdentifier(self, original, swizzled):
+ return 'ade.plexpy.osxnotify'
+
+ def notify(self, subject='', body='', action='', **kwargs):
+
+ subtitle = kwargs.get('subtitle', '')
+ sound = kwargs.get('sound', '')
+ image = kwargs.get('image', '')
+
+ try:
+ self._swizzle(self.objc.lookUpClass('NSBundle'),
+ b'bundleIdentifier',
+ self._swizzled_bundleIdentifier)
+
+ NSUserNotification = self.objc.lookUpClass('NSUserNotification')
+ NSUserNotificationCenter = self.objc.lookUpClass('NSUserNotificationCenter')
+ NSAutoreleasePool = self.objc.lookUpClass('NSAutoreleasePool')
+
+ if not NSUserNotification or not NSUserNotificationCenter:
+ return False
+
+ pool = NSAutoreleasePool.alloc().init()
+
+ notification = NSUserNotification.alloc().init()
+ notification.setTitle_(subject)
+ if subtitle:
+ notification.setSubtitle_(subtitle)
+ if body:
+ notification.setInformativeText_(body)
+ if sound:
+ notification.setSoundName_("NSUserNotificationDefaultSoundName")
+ if image:
+ source_img = self.AppKit.NSImage.alloc().initByReferencingFile_(image)
+ notification.setContentImage_(source_img)
+ # notification.set_identityImage_(source_img)
+ notification.setHasActionButton_(False)
+
+ notification_center = NSUserNotificationCenter.defaultUserNotificationCenter()
+ notification_center.deliverNotification_(notification)
+ logger.info(u"PlexPy Notifiers :: OSX Notify notification sent.")
+
+ del pool
+ return True
+
+ except Exception as e:
+ logger.warn(u"PlexPy Notifiers :: OSX notification failed: %s" % e)
+ return False
+
+ def return_config_options(self):
+ config_option = [{'label': 'Register Notify App',
+ 'value': self.config['notify_app'],
+ 'name': 'osx_notify_app',
+ 'description': 'Enter the path/application name to be registered with the '
+ 'Notification Center, default is /Applications/PlexPy.',
+ 'input_type': 'text'
+ }
+ ]
+
+ return config_option
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index 226de21e..ed880fcc 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -2843,46 +2843,183 @@ class WebInterface(object):
return {'result': 'error', 'message': 'GeoLite2 database uninstall failed.'}
@cherrypy.expose
+ @cherrypy.tools.json_out()
@requireAuth(member_of("admin"))
- def get_notification_agent_config(self, agent_id, **kwargs):
- if agent_id.isdigit():
- config = notifiers.get_notification_agent_config(agent_id=agent_id)
- agents = notifiers.available_notification_agents()
- for agent in agents:
- if int(agent_id) == agent['id']:
- this_agent = agent
- break
- else:
- this_agent = None
- else:
- return None
+ @addtoapi()
+ def get_notifiers(self, notify_action=None, **kwargs):
+ """ Get a list of configured notifiers.
- checkboxes = {'email_tls': checked(plexpy.CONFIG.EMAIL_TLS)}
+ ```
+ Required parameters:
+ None
- return serve_template(templatename="notification_config.html", title="Notification Configuration",
- agent=this_agent, data=config, checkboxes=checkboxes)
+ Optional parameters:
+ notify_action (str): The notification action to filter out
+
+ Returns:
+ json:
+ [{"id": 1,
+ "agent_id": 13,
+ "agent_name": "telegram",
+ "agent_label": "Telegram",
+ "friendly_name": "",
+ "actions": {"on_play": 0,
+ "on_stop": 0,
+ ...
+ },
+ }
+ ]
+ ```
+ """
+ result = notifiers.get_notifiers(notify_action=notify_action)
+ return result
@cherrypy.expose
@requireAuth(member_of("admin"))
- def get_notification_agent_triggers(self, agent_id, **kwargs):
- if agent_id.isdigit():
- agents = notifiers.available_notification_agents()
- for agent in agents:
- if int(agent_id) == agent['id']:
- this_agent = agent
- break
- else:
- this_agent = None
- else:
- return None
+ def get_notifiers_table(self, **kwargs):
+ result = notifiers.get_notifiers()
+ return serve_template(templatename="notifiers_table.html", notifiers_list=result)
- return serve_template(templatename="notification_triggers_modal.html", title="Notification Triggers",
- data=this_agent)
+ @cherrypy.expose
+ @cherrypy.tools.json_out()
+ @requireAuth(member_of("admin"))
+ @addtoapi()
+ def delete_notifier(self, notifier_id=None, **kwargs):
+ """ Remove a notifier from the database.
+
+ ```
+ Required parameters:
+ notifier_id (int): The notifier to delete
+
+ Optional parameters:
+ None
+
+ Returns:
+ None
+ ```
+ """
+ result = notifiers.delete_notifier(notifier_id=notifier_id)
+ if result:
+ return {'result': 'success', 'message': 'Notifier deleted successfully.'}
+ else:
+ return {'result': 'error', 'message': 'Failed to delete notifier.'}
+
+ @cherrypy.expose
+ @cherrypy.tools.json_out()
+ @requireAuth(member_of("admin"))
+ @addtoapi()
+ def get_notifier_config(self, notifier_id=None, **kwargs):
+ """ Get the configuration for an existing notification agent.
+
+ ```
+ Required parameters:
+ notifier_id (int): The notifier config to retrieve
+
+ Optional parameters:
+ None
+
+ Returns:
+ json:
+ {"id": 1,
+ "agent_id": 13,
+ "agent_name": "telegram",
+ "agent_label": "Telegram",
+ "friendly_name": "",
+ "config": {"incl_poster": 0,
+ "html_support": 1,
+ "chat_id": "123456",
+ "bot_token": "13456789:fio9040NNo04jLEp-4S",
+ "incl_subject": 1,
+ "disable_web_preview": 0
+ },
+ "notify_text": {"on_play": {"subject": "...",
+ "body": "..."
+ }
+ "on_stop": {"subject": "...",
+ "body": "..."
+ }
+ ...
+ },
+
+ "actions": {"on_play": 0,
+ "on_stop": 0,
+ ...
+ }
+ }
+ ```
+ """
+ result = notifiers.get_notifier_config(notifier_id=notifier_id)
+ return result
+
+ @cherrypy.expose
+ @requireAuth(member_of("admin"))
+ def get_notifier_config_modal(self, notifier_id=None, **kwargs):
+ result = notifiers.get_notifier_config(notifier_id=notifier_id)
+ return serve_template(templatename="notifier_config.html", notifier=result)
+
+ @cherrypy.expose
+ @cherrypy.tools.json_out()
+ @requireAuth(member_of("admin"))
+ @addtoapi()
+ def add_notifier_config(self, agent_id=None, **kwargs):
+ """ Add a new notification agent.
+
+ ```
+ Required parameters:
+ agent_id (int): The notification agent to add
+
+ Optional parameters:
+ None
+
+ Returns:
+ None
+ ```
+ """
+ result = notifiers.add_notifier_config(agent_id=agent_id, **kwargs)
+
+ if result:
+ return {'result': 'success', 'message': 'Added notification agent.'}
+ else:
+ return {'result': 'error', 'message': 'Failed to add notification agent.'}
+
+ @cherrypy.expose
+ @cherrypy.tools.json_out()
+ @requireAuth(member_of("admin"))
+ @addtoapi()
+ def set_notifier_config(self, notifier_id=None, agent_id=None, **kwargs):
+ """ Configure an exisitng notificaiton agent.
+
+ ```
+ Required parameters:
+ notifier_id (int): The notifier config to update
+ agent_id (int): The agent of the notifier
+
+ Optional parameters:
+ Pass all the config options for the agent with the agent prefix:
+ e.g. For Telegram: telegram_bot_token
+ telegram_chat_id
+ disable_web_preview
+ html_support
+ incl_poster
+ incl_subject
+ Notify actions with 'trigger_' prefix (trigger_on_play, trigger_on_stop, etc.),
+ and notify text with 'text_' prefix (text_on_play_subject, text_on_play_body, etc.) are optional.
+
+ Returns:
+ None
+ ```
+ """
+ result = notifiers.set_notifier_config(notifier_id=notifier_id, agent_id=agent_id, **kwargs)
+
+ if result:
+ return {'result': 'success', 'message': 'Added notification agent.'}
+ else:
+ return {'result': 'error', 'message': 'Failed to add notification agent.'}
@cherrypy.expose
@requireAuth(member_of("admin"))
@addtoapi("notify")
- def send_notification(self, agent_id=None, subject='PlexPy', body='Test notification', notify_action=None, **kwargs):
+ def send_notification(self, notifier_id=None, subject='PlexPy', body='Test notification', notify_action='', **kwargs):
""" Send a notification using PlexPy.
```
@@ -2921,33 +3058,31 @@ class WebInterface(object):
test = 'test ' if notify_action == 'test' else ''
- if agent_id.isdigit():
- agents = notifiers.available_notification_agents()
- for agent in agents:
- if int(agent_id) == agent['id']:
- this_agent = agent
- break
- else:
- this_agent = None
-
- if this_agent:
- logger.debug(u"Sending %s%s notification." % (test, this_agent['name']))
- if notifiers.send_notification(this_agent['id'], subject, body, notify_action, **kwargs):
+ if notifier_id:
+ notifier = notifiers.get_notifier_config(notifier_id=notifier_id)
+
+ if notifier:
+ logger.debug(u"Sending %s%s notification." % (test, notifier['agent_name']))
+ if notifiers.send_notification(notifier_id=notifier_id,
+ subject=subject,
+ body=body,
+ notify_action=notify_action,
+ **kwargs):
return "Notification sent."
else:
return "Notification failed."
else:
- logger.debug(u"Unable to send %snotification, invalid notification agent id %s." % (test, agent_id))
- return "Invalid notification agent id %s." % agent_id
+ logger.debug(u"Unable to send %snotification, invalid notifier_id %s." % (test, notifier_id))
+ return "Invalid notifier id %s." % notifier_id
else:
- logger.debug(u"Unable to send %snotification, no notification agent id received." % test)
- return "No notification agent id received."
+ logger.debug(u"Unable to send %snotification, no notifier_id received." % test)
+ return "No notifier id received."
@cherrypy.expose
@cherrypy.tools.json_out()
@requireAuth(member_of("admin"))
def get_browser_notifications(self, **kwargs):
- browser = notifiers.Browser()
+ browser = notifiers.BROWSER()
result = browser.get_notifications()
if result:
@@ -2964,14 +3099,14 @@ class WebInterface(object):
@requireAuth(member_of("admin"))
def facebookStep1(self, **kwargs):
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
- facebook = notifiers.FacebookNotifier()
+ facebook = notifiers.FACEBOOK()
return facebook._get_authorization()
@cherrypy.expose
@requireAuth(member_of("admin"))
def facebookStep2(self, code, **kwargs):
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
- facebook = notifiers.FacebookNotifier()
+ facebook = notifiers.FACEBOOK()
result = facebook._get_credentials(code)
# logger.info(u"result: " + str(result))
if result:
@@ -2987,8 +3122,8 @@ class WebInterface(object):
result, msg = osxnotify.registerapp(app)
if result:
- osx_notify = notifiers.OSX_NOTIFY()
- osx_notify.notify('Registered', result, 'Success :-)')
+ osx_notify = notifiers.OSX()
+ osx_notify.notify(subject='Registered', body='Success :-)', subtitle=result)
# logger.info(u"Registered %s, to re-register a different app, delete this app first" % result)
else:
logger.warn(msg)