diff --git a/data/interfaces/default/notifier_config.html b/data/interfaces/default/notifier_config.html
index e38e4d09..a23f43c9 100644
--- a/data/interfaces/default/notifier_config.html
+++ b/data/interfaces/default/notifier_config.html
@@ -1,9 +1,9 @@
% if notifier:
-<%!
+<%
import json
from plexpy import notifiers, users
from plexpy.helpers import checked
- available_notification_actions = notifiers.available_notification_actions()
+ available_notification_actions = notifiers.available_notification_actions(agent_id=notifier['agent_id'])
user_emails = [{'user': u['friendly_name'] or u['username'], 'email': u['email']} for u in users.Users().get_users() if u['email']]
sorted(user_emails, key=lambda u: u['user'])
@@ -25,7 +25,7 @@
Arguments
% elif notifier['agent_name'] == 'webhook':
Data
- % else:
+ % elif notifier['agent_name'] != 'plexmobileapp':
Text
% endif
Test Notifications
@@ -684,6 +684,15 @@
pushoverPriority();
});
+ % elif notifier['agent_name'] == 'plexmobileapp':
+ var $plexmobileapp_user_ids = $('#plexmobileapp_user_ids').selectize({
+ plugins: ['remove_button'],
+ maxItems: null,
+ create: true
+ });
+ var plexmobileapp_user_ids = $plexmobileapp_user_ids[0].selectize;
+ plexmobileapp_user_ids.setValue(${json.dumps(next((c['value'] for c in notifier['config_options'] if c['name'] == 'plexmobileapp_user_ids'), [])) | n});
+
% endif
function validateLogic() {
diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py
index 6e2e5b89..8e73e362 100644
--- a/plexpy/notifiers.py
+++ b/plexpy/notifiers.py
@@ -92,7 +92,8 @@ AGENT_IDS = {'growl': 0,
'groupme': 22,
'mqtt': 23,
'zapier': 24,
- 'webhook': 25
+ 'webhook': 25,
+ 'plexmobileapp': 26
}
DEFAULT_CUSTOM_CONDITIONS = [{'parameter': '', 'operator': '', 'value': ''}]
@@ -174,6 +175,11 @@ def available_notification_agents():
'id': AGENT_IDS['plex'],
'action_types': ('all',)
},
+ {'label': 'Plex Mobile App',
+ 'name': 'plexmobileapp',
+ 'id': AGENT_IDS['plexmobileapp'],
+ 'action_types': ('on_play', 'on_created', 'on_newdevice')
+ },
{'label': 'Prowl',
'name': 'prowl',
'id': AGENT_IDS['prowl'],
@@ -440,6 +446,8 @@ def get_agent_class(agent_id=None, config=None):
return ZAPIER(config=config)
elif agent_id == 25:
return WEBHOOK(config=config)
+ elif agent_id == 26:
+ return PLEXMOBILEAPP(config=config)
else:
return Notifier(config=config)
else:
@@ -2628,6 +2636,168 @@ class PLEX(Notifier):
return config_option
+class PLEXMOBILEAPP(Notifier):
+ """
+ Plex Mobile App Notifications
+ """
+ NAME = 'Plex Mobile App'
+ NOTIFICATION_URL = 'https://notifications.plex.tv/api/v1/notifications'
+ _DEFAULT_CONFIG = {'user_ids': [],
+ 'tap_action': 'preplay',
+ }
+
+ def __init__(self, config=None):
+ super(PLEXMOBILEAPP, self).__init__(config=config)
+
+ self.configurations = {
+ 'created': {'group': 'media', 'identifier': 'tv.plex.notification.library.new'},
+ 'play': {'group': 'media', 'identifier': 'tv.plex.notification.playback.started'},
+ 'newdevice': {'group': 'admin', 'identifier': 'tv.plex.notification.device.new'},
+ 'test': {'group': 'media', 'identifier': 'tv.plex.notification.library.new'}
+ }
+
+ def agent_notify(self, subject='', body='', action='', **kwargs):
+ if action not in self.configurations:
+ logger.error(u"Tautulli Notifiers :: Notification action %s not allowed for %s." % (action, self.NAME))
+ return
+
+ headers = {'X-Plex-Token': plexpy.CONFIG.PMS_TOKEN}
+
+ # No subject to always show up regardless of client selected filters
+ # icon can be info, warning, or error
+ # play = true to start playing when tapping the notification
+ # Send the minimal amount of data necessary through Plex servers
+ data = {
+ 'group': self.configurations[action]['group'],
+ 'identifier': self.configurations[action]['identifier'],
+ 'to': self.config['user_ids'],
+ 'data': {
+ 'provider': {
+ 'identifier': plexpy.CONFIG.PMS_IDENTIFIER,
+ 'title': plexpy.CONFIG.PMS_NAME
+ }
+ }
+ }
+
+ pretty_metadata = PrettyMetadata(kwargs['parameters'])
+
+ if action == 'test':
+ data['metadata'] = {
+ 'type': 'movie',
+ 'title': subject,
+ 'year': body
+ }
+
+ elif action in ('play', 'newdevice'):
+ data['data']['player'] = {
+ 'title': pretty_metadata.parameters['player'],
+ 'platform': pretty_metadata.parameters['platform'],
+ 'machineIdentifier': pretty_metadata.parameters['machine_id']
+ }
+ data['data']['user'] = {
+ 'title': pretty_metadata.parameters['user'],
+ 'id': pretty_metadata.parameters['user_id'],
+ 'thumb': pretty_metadata.parameters['user_thumb'],
+ }
+
+ elif action == 'created':
+ # No addition data required for recently added
+ pass
+
+ else:
+ return
+
+ if data['group'] == 'media' and action != 'test':
+ media_type = pretty_metadata.media_type
+ uri_rating_key = None
+
+ if media_type == 'movie':
+ metadata = {
+ 'type': media_type,
+ 'title': pretty_metadata.parameters['title'],
+ 'year': pretty_metadata.parameters['year'],
+ 'thumb': pretty_metadata.parameters['thumb']
+ }
+ elif media_type == 'show':
+ metadata = {
+ 'type': media_type,
+ 'title': pretty_metadata.parameters['show_name'],
+ 'thumb': pretty_metadata.parameters['thumb']
+ }
+ elif media_type == 'season':
+ metadata = {
+ 'type': 'show',
+ 'title': pretty_metadata.parameters['show_name'],
+ 'thumb': pretty_metadata.parameters['thumb'],
+ }
+ elif media_type == 'episode':
+ metadata = {
+ 'type': media_type,
+ 'title': pretty_metadata.parameters['episode_name'],
+ 'grandparentTitle': pretty_metadata.parameters['show_name'],
+ 'index': pretty_metadata.parameters['episode_num'],
+ 'parentIndex': pretty_metadata.parameters['season_num'],
+ 'grandparentThumb': pretty_metadata.parameters['grandparent_thumb']
+ }
+ elif media_type == 'artist':
+ metadata = {
+ 'type': media_type,
+ 'title': pretty_metadata.parameters['artist_name'],
+ 'thumb': pretty_metadata.parameters['thumb']
+ }
+ elif media_type == 'album':
+ metadata = {
+ 'type': media_type,
+ 'title': pretty_metadata.parameters['album_name'],
+ 'parentTitle': pretty_metadata.parameters['artist_name'],
+ 'thumb': pretty_metadata.parameters['thumb'],
+ }
+ elif media_type == 'track':
+ metadata = {
+ 'type': 'album',
+ 'title': pretty_metadata.parameters['album_name'],
+ 'parentTitle': pretty_metadata.parameters['artist_name'],
+ 'thumb': pretty_metadata.parameters['parent_thumb']
+ }
+ uri_rating_key = pretty_metadata.parameters['parent_rating_key']
+ else:
+ logger.error(u"Tautulli Notifiers :: Media type %s not supported for %s." % (media_type, self.NAME))
+ return
+
+ data['metadata'] = metadata
+ data['uri'] = 'server://{}/com.plexapp.plugins.library/library/metadata/{}'.format(
+ plexpy.CONFIG.PMS_IDENTIFIER, uri_rating_key or pretty_metadata.parameters['rating_key']
+ )
+ data['play'] = self.config['tap_action'] == 'play'
+
+ return self.make_request(self.NOTIFICATION_URL, headers=headers, json=data)
+
+ def get_users(self):
+ user_ids = {u['user_id']: u['friendly_name'] for u in users.Users().get_users() if u['user_id']}
+ user_ids[''] = ''
+ return user_ids
+
+ def _return_config_options(self):
+ config_option = [{'label': 'Plex User(s)',
+ 'value': self.config['user_ids'],
+ 'name': 'plexmobileapp_user_ids',
+ 'description': 'Select which Plex User(s) to receive notifications.',
+ 'input_type': 'select',
+ 'select_options': self.get_users()
+ },
+ {'label': 'Notification Tap Action',
+ 'value': self.config['tap_action'],
+ 'name': 'plexmobileapp_tap_action',
+ 'description': 'Set the action when tapping on the notification.',
+ 'input_type': 'select',
+ 'select_options': {'preplay': 'Go to media pre-play screen',
+ 'play': 'Start playing the media'}
+ },
+ ]
+
+ return config_option
+
+
class PROWL(Notifier):
"""
Prowl notifications.