2017-02-11 04:08:36 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2017-02-20 05:37:00 +00:00
|
|
|
import json
|
|
|
|
import threading
|
2017-02-26 23:17:03 +00:00
|
|
|
import websocket
|
2017-02-11 04:08:36 +00:00
|
|
|
from plexapi import log
|
|
|
|
|
|
|
|
|
2017-02-25 04:50:58 +00:00
|
|
|
class AlertListener(threading.Thread):
|
2019-12-04 19:15:51 +00:00
|
|
|
""" Creates a websocket connection to the PlexServer to optionally receive alert notifications.
|
2017-02-25 04:50:58 +00:00
|
|
|
These often include messages from Plex about media scans as well as updates to currently running
|
2019-12-04 19:15:51 +00:00
|
|
|
Transcode Sessions. This class implements threading.Thread, therefore to start monitoring
|
2017-02-25 04:50:58 +00:00
|
|
|
alerts you must call .start() on the object once it's created. When calling
|
|
|
|
`PlexServer.startAlertListener()`, the thread will be started for you.
|
2017-02-24 16:18:54 +00:00
|
|
|
|
2018-09-14 18:03:23 +00:00
|
|
|
Known `state`-values for timeline entries, with identifier=`com.plexapp.plugins.library`:
|
|
|
|
|
|
|
|
:0: The item was created
|
|
|
|
:1: Reporting progress on item processing
|
|
|
|
:2: Matching the item
|
|
|
|
:3: Downloading the metadata
|
|
|
|
:4: Processing downloaded metadata
|
|
|
|
:5: The item processed
|
|
|
|
:9: The item deleted
|
|
|
|
|
|
|
|
When metadata agent is not set for the library processing ends with state=1.
|
|
|
|
|
2017-02-13 02:55:55 +00:00
|
|
|
Parameters:
|
2017-02-25 04:50:58 +00:00
|
|
|
server (:class:`~plexapi.server.PlexServer`): PlexServer this listener is connected to.
|
2019-12-04 19:15:51 +00:00
|
|
|
callback (func): Callback function to call on received messages. The callback function
|
2017-02-24 16:18:54 +00:00
|
|
|
will be sent a single argument 'data' which will contain a dictionary of data
|
2019-12-04 19:15:51 +00:00
|
|
|
received from the server. :samp:`def my_callback(data): ...`
|
2017-02-11 04:08:36 +00:00
|
|
|
"""
|
|
|
|
key = '/:/websockets/notifications'
|
|
|
|
|
|
|
|
def __init__(self, server, callback=None):
|
2017-04-30 01:19:59 +00:00
|
|
|
super(AlertListener, self).__init__()
|
|
|
|
self.daemon = True
|
2017-02-11 04:08:36 +00:00
|
|
|
self._server = server
|
|
|
|
self._callback = callback
|
|
|
|
self._ws = None
|
|
|
|
|
|
|
|
def run(self):
|
2017-02-11 04:26:09 +00:00
|
|
|
# create the websocket connection
|
2018-01-05 02:44:35 +00:00
|
|
|
url = self._server.url(self.key, includeToken=True).replace('http', 'ws')
|
2017-02-25 04:50:58 +00:00
|
|
|
log.info('Starting AlertListener: %s', url)
|
2017-02-27 02:09:29 +00:00
|
|
|
self._ws = websocket.WebSocketApp(url, on_message=self._onMessage,
|
2017-10-26 18:22:35 +00:00
|
|
|
on_error=self._onError)
|
2017-02-11 04:08:36 +00:00
|
|
|
self._ws.run_forever()
|
|
|
|
|
|
|
|
def stop(self):
|
2019-12-04 19:15:51 +00:00
|
|
|
""" Stop the AlertListener thread. Once the notifier is stopped, it cannot be directly
|
2017-02-25 04:50:58 +00:00
|
|
|
started again. You must call :func:`plexapi.server.PlexServer.startAlertListener()`
|
2017-02-24 16:18:54 +00:00
|
|
|
from a PlexServer instance.
|
|
|
|
"""
|
2017-02-25 04:50:58 +00:00
|
|
|
log.info('Stopping AlertListener.')
|
2017-02-11 04:08:36 +00:00
|
|
|
self._ws.close()
|
|
|
|
|
2019-12-04 18:53:30 +00:00
|
|
|
def _onMessage(self, *args):
|
2019-12-04 19:15:51 +00:00
|
|
|
""" Called when websocket message is received.
|
2019-12-31 13:06:56 +00:00
|
|
|
In earlier releases, websocket-client returned a tuple of two parameters: a websocket.app.WebSocketApp
|
|
|
|
object and the message as a STR. Current releases appear to only return the message.
|
2019-12-04 19:14:29 +00:00
|
|
|
We are assuming the last argument in the tuple is the message.
|
2019-12-31 13:06:56 +00:00
|
|
|
This is to support compatibility with current and previous releases of websocket-client.
|
2019-12-04 19:14:29 +00:00
|
|
|
"""
|
2019-12-04 18:53:30 +00:00
|
|
|
message = args[-1]
|
2017-02-11 04:08:36 +00:00
|
|
|
try:
|
|
|
|
data = json.loads(message)['NotificationContainer']
|
2017-10-25 22:51:25 +00:00
|
|
|
log.debug('Alert: %s %s %s', *data)
|
2017-02-11 04:08:36 +00:00
|
|
|
if self._callback:
|
|
|
|
self._callback(data)
|
2017-10-26 18:22:35 +00:00
|
|
|
except Exception as err: # pragma: no cover
|
2017-02-25 04:50:58 +00:00
|
|
|
log.error('AlertListener Msg Error: %s', err)
|
2017-02-11 04:08:36 +00:00
|
|
|
|
2019-12-04 18:53:30 +00:00
|
|
|
def _onError(self, *args): # pragma: no cover
|
2019-12-04 19:15:51 +00:00
|
|
|
""" Called when websocket error is received.
|
2019-12-31 13:06:56 +00:00
|
|
|
In earlier releases, websocket-client returned a tuple of two parameters: a websocket.app.WebSocketApp
|
|
|
|
object and the error. Current releases appear to only return the error.
|
|
|
|
We are assuming the last argument in the tuple is the message.
|
|
|
|
This is to support compatibility with current and previous releases of websocket-client.
|
2019-12-04 19:14:29 +00:00
|
|
|
"""
|
2019-12-04 18:53:30 +00:00
|
|
|
err = args[-1]
|
2017-02-25 04:50:58 +00:00
|
|
|
log.error('AlertListener Error: %s' % err)
|