or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

account-management.mdalert-system.mdclient-control.mdindex.mdmedia-library.mdplaylists-collections.mdserver-connection.mdsync-management.md

alert-system.mddocs/

0

# Real-time Server Monitoring

1

2

WebSocket-based real-time monitoring of server events, activities, and notifications for building responsive Plex applications that react immediately to server changes.

3

4

## Capabilities

5

6

### AlertListener Setup

7

8

Primary class for establishing WebSocket connections and receiving real-time server notifications.

9

10

```python { .api }

11

class AlertListener:

12

def __init__(self, server, callback, ws_url=None, **kwargs):

13

"""

14

Create real-time alert listener for server events.

15

16

Args:

17

server (PlexServer): Connected Plex server

18

callback (function): Function to handle incoming alerts

19

ws_url (str, optional): Custom WebSocket URL

20

**kwargs: Additional WebSocket connection parameters

21

"""

22

23

def start(self):

24

"""Start listening for server alerts in background thread."""

25

26

def stop(self):

27

"""Stop listening and close WebSocket connection."""

28

29

def is_running(self):

30

"""

31

Check if alert listener is actively running.

32

33

Returns:

34

bool: True if listener is active

35

"""

36

```

37

38

### Alert Event Types

39

40

Server events that can be monitored through the AlertListener system.

41

42

```python { .api }

43

# Common alert event types received via callback

44

class AlertEvent:

45

"""Alert event data structure."""

46

47

@property

48

def type(self):

49

"""str: Event type identifier."""

50

51

@property

52

def data(self):

53

"""dict: Event-specific data payload."""

54

55

@property

56

def timestamp(self):

57

"""datetime: When the event occurred."""

58

59

# Event type constants

60

ALERT_TYPES = {

61

'library.refresh.started': 'Library refresh started',

62

'library.refresh.progress': 'Library refresh progress update',

63

'library.refresh.finished': 'Library refresh completed',

64

'media.scrobble': 'Media playback scrobble event',

65

'media.play': 'Media playback started',

66

'media.pause': 'Media playback paused',

67

'media.resume': 'Media playback resumed',

68

'media.stop': 'Media playback stopped',

69

'admin.database.backup': 'Database backup event',

70

'admin.database.corrupted': 'Database corruption detected',

71

'transcoder.progress': 'Transcoding progress update',

72

'activity': 'Background activity update'

73

}

74

```

75

76

### Callback Function Interface

77

78

Structure for handling incoming alert events.

79

80

```python { .api }

81

def alert_callback(alert_data):

82

"""

83

Callback function for processing server alerts.

84

85

Args:

86

alert_data (dict): Raw alert data from server containing:

87

- type (str): Alert event type

88

- NotificationContainer (dict): Event details

89

- size (int): Number of notifications

90

- data (list): List of notification objects

91

"""

92

93

# Example callback implementation structure

94

def process_alert(alert_data):

95

alert_type = alert_data.get('type')

96

container = alert_data.get('NotificationContainer', {})

97

98

if alert_type == 'playing':

99

# Handle playback events

100

sessions = container.get('PlaySessionStateNotification', [])

101

for session in sessions:

102

handle_playback_event(session)

103

104

elif alert_type == 'timeline':

105

# Handle timeline updates

106

entries = container.get('TimelineEntry', [])

107

for entry in entries:

108

handle_timeline_update(entry)

109

110

elif alert_type == 'activity':

111

# Handle background activities

112

activities = container.get('ActivityNotification', [])

113

for activity in activities:

114

handle_activity_update(activity)

115

```

116

117

## Usage Examples

118

119

### Basic Alert Monitoring

120

121

```python

122

from plexapi.server import PlexServer

123

from plexapi.alert import AlertListener

124

125

plex = PlexServer('http://localhost:32400', token='your-token')

126

127

def my_callback(alert_data):

128

"""Handle server alerts."""

129

alert_type = alert_data.get('type', 'unknown')

130

print(f"Alert received: {alert_type}")

131

132

if alert_type == 'playing':

133

# Handle playback state changes

134

container = alert_data.get('NotificationContainer', {})

135

sessions = container.get('PlaySessionStateNotification', [])

136

137

for session in sessions:

138

state = session.get('state', '')

139

session_key = session.get('sessionKey', '')

140

print(f"Session {session_key}: {state}")

141

142

# Create and start listener

143

listener = AlertListener(plex, my_callback)

144

listener.start()

145

146

# Keep program running to receive alerts

147

try:

148

while True:

149

time.sleep(1)

150

except KeyboardInterrupt:

151

listener.stop()

152

```

153

154

### Playback Monitoring

155

156

```python

157

def playback_monitor(alert_data):

158

"""Monitor media playback events."""

159

if alert_data.get('type') == 'playing':

160

container = alert_data.get('NotificationContainer', {})

161

sessions = container.get('PlaySessionStateNotification', [])

162

163

for session in sessions:

164

user = session.get('username', 'Unknown')

165

state = session.get('state', '')

166

title = session.get('title', 'Unknown')

167

168

if state == 'playing':

169

print(f"{user} started playing: {title}")

170

elif state == 'paused':

171

print(f"{user} paused: {title}")

172

elif state == 'stopped':

173

print(f"{user} stopped: {title}")

174

175

listener = AlertListener(plex, playback_monitor)

176

listener.start()

177

```

178

179

### Library Activity Monitoring

180

181

```python

182

def library_monitor(alert_data):

183

"""Monitor library scanning and updates."""

184

alert_type = alert_data.get('type', '')

185

186

if alert_type == 'activity':

187

container = alert_data.get('NotificationContainer', {})

188

activities = container.get('ActivityNotification', [])

189

190

for activity in activities:

191

activity_type = activity.get('type', '')

192

title = activity.get('title', '')

193

progress = activity.get('progress', 0)

194

195

if activity_type == 'library.refresh':

196

print(f"Library scan: {title} - {progress}% complete")

197

elif activity_type == 'transcoder':

198

print(f"Transcoding: {title} - {progress}% complete")

199

200

listener = AlertListener(plex, library_monitor)

201

listener.start()

202

```

203

204

### Timeline Event Monitoring

205

206

```python

207

def timeline_monitor(alert_data):

208

"""Monitor media timeline updates."""

209

if alert_data.get('type') == 'timeline':

210

container = alert_data.get('NotificationContainer', {})

211

entries = container.get('TimelineEntry', [])

212

213

for entry in entries:

214

item_type = entry.get('type', '')

215

state = entry.get('state', '')

216

title = entry.get('title', 'Unknown')

217

218

if state == 'created':

219

print(f"New {item_type} added: {title}")

220

elif state == 'updated':

221

print(f"{item_type} updated: {title}")

222

elif state == 'deleted':

223

print(f"{item_type} deleted: {title}")

224

225

listener = AlertListener(plex, timeline_monitor)

226

listener.start()

227

```

228

229

### Multi-Event Handler

230

231

```python

232

class PlexEventHandler:

233

"""Comprehensive event handler for multiple alert types."""

234

235

def __init__(self, plex_server):

236

self.plex = plex_server

237

self.listener = AlertListener(plex_server, self.handle_alert)

238

239

def handle_alert(self, alert_data):

240

"""Route alerts to specific handlers."""

241

alert_type = alert_data.get('type', '')

242

243

if alert_type == 'playing':

244

self.handle_playback(alert_data)

245

elif alert_type == 'timeline':

246

self.handle_timeline(alert_data)

247

elif alert_type == 'activity':

248

self.handle_activity(alert_data)

249

else:

250

print(f"Unhandled alert type: {alert_type}")

251

252

def handle_playback(self, alert_data):

253

"""Handle playback state changes."""

254

container = alert_data.get('NotificationContainer', {})

255

sessions = container.get('PlaySessionStateNotification', [])

256

257

for session in sessions:

258

self.log_playback_event(session)

259

260

def handle_timeline(self, alert_data):

261

"""Handle library timeline updates."""

262

container = alert_data.get('NotificationContainer', {})

263

entries = container.get('TimelineEntry', [])

264

265

for entry in entries:

266

self.log_timeline_event(entry)

267

268

def handle_activity(self, alert_data):

269

"""Handle background activity updates."""

270

container = alert_data.get('NotificationContainer', {})

271

activities = container.get('ActivityNotification', [])

272

273

for activity in activities:

274

self.log_activity_event(activity)

275

276

def log_playback_event(self, session):

277

"""Log playback events to file or database."""

278

timestamp = datetime.now()

279

user = session.get('username', 'Unknown')

280

state = session.get('state', '')

281

title = session.get('title', 'Unknown')

282

283

log_entry = f"{timestamp}: {user} {state} {title}"

284

print(log_entry)

285

# Save to database or log file

286

287

def log_timeline_event(self, entry):

288

"""Log timeline events."""

289

# Implementation for timeline logging

290

pass

291

292

def log_activity_event(self, activity):

293

"""Log activity events."""

294

# Implementation for activity logging

295

pass

296

297

def start(self):

298

"""Start monitoring."""

299

self.listener.start()

300

print("Event monitoring started")

301

302

def stop(self):

303

"""Stop monitoring."""

304

self.listener.stop()

305

print("Event monitoring stopped")

306

307

# Usage

308

handler = PlexEventHandler(plex)

309

handler.start()

310

311

# Run until interrupted

312

try:

313

while True:

314

time.sleep(1)

315

except KeyboardInterrupt:

316

handler.stop()

317

```

318

319

### Error Handling and Reconnection

320

321

```python

322

import time

323

import logging

324

from plexapi.alert import AlertListener

325

from plexapi.exceptions import PlexApiException

326

327

class RobustAlertListener:

328

"""Alert listener with automatic reconnection."""

329

330

def __init__(self, server, callback, max_retries=5):

331

self.server = server

332

self.callback = callback

333

self.max_retries = max_retries

334

self.retry_count = 0

335

self.listener = None

336

self.running = False

337

338

def start(self):

339

"""Start listening with automatic retry on failure."""

340

self.running = True

341

while self.running and self.retry_count < self.max_retries:

342

try:

343

self.listener = AlertListener(self.server, self.callback)

344

self.listener.start()

345

self.retry_count = 0 # Reset on successful connection

346

break

347

348

except PlexApiException as e:

349

self.retry_count += 1

350

wait_time = min(2 ** self.retry_count, 60) # Exponential backoff

351

logging.error(f"Alert listener failed: {e}. Retrying in {wait_time}s...")

352

time.sleep(wait_time)

353

354

def stop(self):

355

"""Stop listening."""

356

self.running = False

357

if self.listener:

358

self.listener.stop()

359

360

# Usage with robust error handling

361

robust_listener = RobustAlertListener(plex, my_callback)

362

robust_listener.start()

363

```

364

365

### Performance Considerations

366

367

```python

368

def efficient_callback(alert_data):

369

"""Efficiently handle high-volume alerts."""

370

# Process alerts quickly to avoid blocking

371

alert_type = alert_data.get('type', '')

372

373

# Queue heavy processing for background thread

374

if alert_type == 'playing':

375

# Quick processing only

376

session_count = len(alert_data.get('NotificationContainer', {}).get('PlaySessionStateNotification', []))

377

print(f"Active sessions: {session_count}")

378

379

# For complex processing, consider using a queue

380

# alert_queue.put(alert_data)

381

382

# Optional: Use threading for heavy processing

383

import queue

384

import threading

385

386

alert_queue = queue.Queue()

387

388

def background_processor():

389

"""Process alerts in background thread."""

390

while True:

391

try:

392

alert_data = alert_queue.get(timeout=1)

393

# Perform heavy processing here

394

process_complex_alert(alert_data)

395

alert_queue.task_done()

396

except queue.Empty:

397

continue

398

399

# Start background processor

400

processor_thread = threading.Thread(target=background_processor, daemon=True)

401

processor_thread.start()

402

```

403

404

## Installation Requirements

405

406

The AlertListener functionality requires the optional WebSocket dependency:

407

408

```bash

409

pip install PlexAPI[alert]

410

```

411

412

This installs the `websocket-client` package required for real-time WebSocket connections to the Plex server.