0
# Event Client
1
2
The EventClient provides an asynchronous interface for receiving and handling real-time events from OBS Studio. It uses a callback-based system where you register handler functions that are triggered when specific events occur.
3
4
## Core Event System
5
6
### Event Client Initialization
7
8
```python { .api }
9
class EventClient:
10
def __init__(self, host='localhost', port=4455, password='', subs=Subs.LOW_VOLUME, timeout=None):
11
"""
12
Initialize event client.
13
14
Parameters:
15
- host (str): OBS WebSocket host address
16
- port (int): OBS WebSocket port number
17
- password (str): OBS WebSocket password
18
- subs (int): Event subscription flags (Subs enum values)
19
- timeout (float, optional): Connection timeout in seconds
20
"""
21
22
def __enter__(self):
23
"""Context manager entry."""
24
25
def __exit__(self, exc_type, exc_value, exc_traceback):
26
"""Context manager exit with automatic disconnect."""
27
28
def disconnect(self):
29
"""Stop listening for events and close connection."""
30
31
unsubscribe = disconnect # Alias for disconnect
32
```
33
34
### Callback Management
35
36
The EventClient provides a `callback` property that manages event handler registration:
37
38
```python { .api }
39
class Callback:
40
def register(self, fns):
41
"""
42
Register callback function(s) for events.
43
44
Parameters:
45
- fns (callable or list): Single function or list of functions to register
46
"""
47
48
def deregister(self, fns):
49
"""
50
Deregister callback function(s).
51
52
Parameters:
53
- fns (callable or list): Single function or list of functions to deregister
54
"""
55
56
def get(self):
57
"""
58
Get list of registered event names.
59
60
Returns:
61
List of event names (CamelCase) that have registered handlers
62
"""
63
64
def clear(self):
65
"""Clear all registered callback functions."""
66
```
67
68
## Event Handler Conventions
69
70
Event handler functions must follow specific naming conventions:
71
72
- Use **snake_case** naming with **"on_"** prefix
73
- Function name must match the OBS event name converted to snake_case
74
- Handler receives a single `data` parameter containing event information
75
76
### Event Name Mapping
77
78
OBS Event Name → Handler Function Name:
79
- `SceneCreated` → `on_scene_created`
80
- `InputMuteStateChanged` → `on_input_mute_state_changed`
81
- `CurrentProgramSceneChanged` → `on_current_program_scene_changed`
82
- `StreamStateChanged` → `on_stream_state_changed`
83
84
### Event Data Access
85
86
Event data is provided as dataclass objects with snake_case attributes:
87
88
```python
89
def on_scene_created(data):
90
# Access event attributes
91
print(f"Scene created: {data.scene_name}")
92
print(f"Is group: {data.is_group}")
93
94
# Inspect all available attributes
95
print(f"Available attributes: {data.attrs()}")
96
```
97
98
## Common Event Categories
99
100
### Scene Events
101
102
```python
103
def on_scene_created(data):
104
"""
105
Triggered when a scene is created.
106
107
Event data attributes:
108
- scene_name (str): Name of created scene
109
- is_group (bool): Whether scene is a group
110
"""
111
112
def on_scene_removed(data):
113
"""
114
Triggered when a scene is removed.
115
116
Event data attributes:
117
- scene_name (str): Name of removed scene
118
- is_group (bool): Whether scene was a group
119
"""
120
121
def on_scene_name_changed(data):
122
"""
123
Triggered when a scene is renamed.
124
125
Event data attributes:
126
- old_scene_name (str): Previous scene name
127
- scene_name (str): New scene name
128
"""
129
130
def on_current_program_scene_changed(data):
131
"""
132
Triggered when program scene changes.
133
134
Event data attributes:
135
- scene_name (str): New program scene name
136
"""
137
138
def on_current_preview_scene_changed(data):
139
"""
140
Triggered when preview scene changes.
141
142
Event data attributes:
143
- scene_name (str): New preview scene name
144
"""
145
146
def on_scene_list_changed(data):
147
"""
148
Triggered when scene list changes.
149
150
Event data attributes:
151
- scenes (list): Updated scene list
152
"""
153
```
154
155
### Input Events
156
157
```python
158
def on_input_created(data):
159
"""
160
Triggered when an input is created.
161
162
Event data attributes:
163
- input_name (str): Name of created input
164
- input_kind (str): Type/kind of input
165
- unversioned_input_kind (str): Unversioned input kind
166
- input_settings (dict): Input settings
167
- default_input_settings (dict): Default settings
168
"""
169
170
def on_input_removed(data):
171
"""
172
Triggered when an input is removed.
173
174
Event data attributes:
175
- input_name (str): Name of removed input
176
"""
177
178
def on_input_name_changed(data):
179
"""
180
Triggered when input is renamed.
181
182
Event data attributes:
183
- old_input_name (str): Previous input name
184
- input_name (str): New input name
185
"""
186
187
def on_input_mute_state_changed(data):
188
"""
189
Triggered when input mute state changes.
190
191
Event data attributes:
192
- input_name (str): Input name
193
- input_muted (bool): New mute state
194
"""
195
196
def on_input_volume_changed(data):
197
"""
198
Triggered when input volume changes.
199
200
Event data attributes:
201
- input_name (str): Input name
202
- input_volume_mul (float): Volume multiplier
203
- input_volume_db (float): Volume in dB
204
"""
205
206
def on_input_settings_changed(data):
207
"""
208
Triggered when input settings change.
209
210
Event data attributes:
211
- input_name (str): Input name
212
- input_settings (dict): New input settings
213
"""
214
```
215
216
### Stream and Record Events
217
218
```python
219
def on_stream_state_changed(data):
220
"""
221
Triggered when streaming state changes.
222
223
Event data attributes:
224
- output_active (bool): Stream active state
225
- output_state (str): Stream state name
226
"""
227
228
def on_record_state_changed(data):
229
"""
230
Triggered when recording state changes.
231
232
Event data attributes:
233
- output_active (bool): Recording active state
234
- output_state (str): Recording state name
235
- output_path (str, optional): Recording file path
236
"""
237
238
def on_record_file_changed(data):
239
"""
240
Triggered when recording file changes.
241
242
Event data attributes:
243
- new_output_path (str): New recording file path
244
"""
245
246
def on_replay_buffer_state_changed(data):
247
"""
248
Triggered when replay buffer state changes.
249
250
Event data attributes:
251
- output_active (bool): Replay buffer active state
252
- output_state (str): Replay buffer state name
253
"""
254
255
def on_replay_buffer_saved(data):
256
"""
257
Triggered when replay buffer is saved.
258
259
Event data attributes:
260
- saved_replay_path (str): Path to saved replay file
261
"""
262
```
263
264
### Scene Item Events
265
266
```python
267
def on_scene_item_created(data):
268
"""
269
Triggered when scene item is created.
270
271
Event data attributes:
272
- scene_name (str): Scene name
273
- source_name (str): Source name
274
- scene_item_id (int): Scene item ID
275
- scene_item_index (int): Scene item index
276
"""
277
278
def on_scene_item_removed(data):
279
"""
280
Triggered when scene item is removed.
281
282
Event data attributes:
283
- scene_name (str): Scene name
284
- source_name (str): Source name
285
- scene_item_id (int): Scene item ID
286
"""
287
288
def on_scene_item_enable_state_changed(data):
289
"""
290
Triggered when scene item enable state changes.
291
292
Event data attributes:
293
- scene_name (str): Scene name
294
- scene_item_id (int): Scene item ID
295
- scene_item_enabled (bool): New enable state
296
"""
297
298
def on_scene_item_lock_state_changed(data):
299
"""
300
Triggered when scene item lock state changes.
301
302
Event data attributes:
303
- scene_name (str): Scene name
304
- scene_item_id (int): Scene item ID
305
- scene_item_locked (bool): New lock state
306
"""
307
308
def on_scene_item_transform_changed(data):
309
"""
310
Triggered when scene item transform changes (HIGH_VOLUME event).
311
312
Event data attributes:
313
- scene_name (str): Scene name
314
- scene_item_id (int): Scene item ID
315
- scene_item_transform (dict): New transform data
316
"""
317
```
318
319
### Filter Events
320
321
```python
322
def on_source_filter_created(data):
323
"""
324
Triggered when source filter is created.
325
326
Event data attributes:
327
- source_name (str): Source name
328
- filter_name (str): Filter name
329
- filter_kind (str): Filter type
330
- filter_index (int): Filter index
331
- filter_settings (dict): Filter settings
332
- default_filter_settings (dict): Default settings
333
"""
334
335
def on_source_filter_removed(data):
336
"""
337
Triggered when source filter is removed.
338
339
Event data attributes:
340
- source_name (str): Source name
341
- filter_name (str): Filter name
342
"""
343
344
def on_source_filter_enable_state_changed(data):
345
"""
346
Triggered when filter enable state changes.
347
348
Event data attributes:
349
- source_name (str): Source name
350
- filter_name (str): Filter name
351
- filter_enabled (bool): New enable state
352
"""
353
```
354
355
## Usage Examples
356
357
### Basic Event Listening
358
359
```python
360
import obsws_python as obs
361
362
# Create event client with low-volume events
363
client = obs.EventClient(subs=obs.Subs.LOW_VOLUME)
364
365
# Define event handlers
366
def on_scene_created(data):
367
print(f"New scene created: {data.scene_name}")
368
369
def on_input_mute_state_changed(data):
370
print(f"Input '{data.input_name}' mute changed to: {data.input_muted}")
371
372
def on_stream_state_changed(data):
373
if data.output_active:
374
print("Stream started!")
375
else:
376
print("Stream stopped!")
377
378
# Register callbacks
379
client.callback.register([
380
on_scene_created,
381
on_input_mute_state_changed,
382
on_stream_state_changed
383
])
384
385
# Keep client running
386
try:
387
input("Press Enter to stop listening...\n")
388
finally:
389
client.disconnect()
390
```
391
392
### Event Logging System
393
394
```python
395
import obsws_python as obs
396
import logging
397
from datetime import datetime
398
399
# Setup logging
400
logging.basicConfig(level=logging.INFO)
401
logger = logging.getLogger(__name__)
402
403
def log_event(event_name):
404
"""Decorator to log event occurrences."""
405
def decorator(func):
406
def wrapper(data):
407
timestamp = datetime.now().strftime("%H:%M:%S")
408
logger.info(f"[{timestamp}] {event_name}: {data.attrs()}")
409
return func(data)
410
return wrapper
411
return decorator
412
413
# Event handlers with logging
414
@log_event("Scene Changed")
415
def on_current_program_scene_changed(data):
416
print(f"Switched to scene: {data.scene_name}")
417
418
@log_event("Input Created")
419
def on_input_created(data):
420
print(f"New input: {data.input_name} ({data.input_kind})")
421
422
@log_event("Recording State")
423
def on_record_state_changed(data):
424
state = "started" if data.output_active else "stopped"
425
print(f"Recording {state}")
426
if hasattr(data, 'output_path') and data.output_path:
427
print(f"Recording to: {data.output_path}")
428
429
# Initialize client and register handlers
430
with obs.EventClient(subs=obs.Subs.LOW_VOLUME) as client:
431
client.callback.register([
432
on_current_program_scene_changed,
433
on_input_created,
434
on_record_state_changed
435
])
436
437
print("Event logging started. Registered events:")
438
for event in client.callback.get():
439
print(f" - {event}")
440
441
input("Press Enter to stop...\n")
442
```
443
444
### High-Volume Event Monitoring
445
446
```python
447
import obsws_python as obs
448
import time
449
450
# Monitor high-frequency events
451
client = obs.EventClient(subs=obs.Subs.HIGH_VOLUME)
452
453
volume_updates = 0
454
transform_updates = 0
455
456
def on_input_volume_meters(data):
457
"""Handle volume meter updates (very frequent)."""
458
global volume_updates
459
volume_updates += 1
460
461
# Log every 100 updates to avoid spam
462
if volume_updates % 100 == 0:
463
print(f"Volume meter updates: {volume_updates}")
464
465
def on_scene_item_transform_changed(data):
466
"""Handle scene item transform changes (frequent during dragging)."""
467
global transform_updates
468
transform_updates += 1
469
470
if transform_updates % 10 == 0:
471
print(f"Transform updates: {transform_updates} (Item {data.scene_item_id})")
472
473
def on_input_active_state_changed(data):
474
"""Handle input active state changes."""
475
state = "active" if data.input_active else "inactive"
476
print(f"Input '{data.input_name}' is now {state}")
477
478
client.callback.register([
479
on_input_volume_meters,
480
on_scene_item_transform_changed,
481
on_input_active_state_changed
482
])
483
484
print("Monitoring high-volume events...")
485
print("Warning: This will generate many events!")
486
487
try:
488
# Run for 30 seconds
489
time.sleep(30)
490
finally:
491
client.disconnect()
492
print(f"Final counts - Volume: {volume_updates}, Transform: {transform_updates}")
493
```
494
495
### Dynamic Event Registration
496
497
```python
498
import obsws_python as obs
499
500
class OBSEventManager:
501
def __init__(self):
502
self.client = obs.EventClient(subs=obs.Subs.LOW_VOLUME)
503
self.handlers = {}
504
505
def add_scene_monitoring(self):
506
"""Add scene-related event handlers."""
507
def on_scene_created(data):
508
print(f"📄 Scene created: {data.scene_name}")
509
510
def on_current_program_scene_changed(data):
511
print(f"🎬 Now showing: {data.scene_name}")
512
513
self.handlers['scenes'] = [on_scene_created, on_current_program_scene_changed]
514
self.client.callback.register(self.handlers['scenes'])
515
print("Scene monitoring enabled")
516
517
def add_input_monitoring(self):
518
"""Add input-related event handlers."""
519
def on_input_mute_state_changed(data):
520
emoji = "🔇" if data.input_muted else "🔊"
521
print(f"{emoji} {data.input_name} mute: {data.input_muted}")
522
523
def on_input_volume_changed(data):
524
print(f"🎚️ {data.input_name} volume: {data.input_volume_db:.1f}dB")
525
526
self.handlers['inputs'] = [on_input_mute_state_changed, on_input_volume_changed]
527
self.client.callback.register(self.handlers['inputs'])
528
print("Input monitoring enabled")
529
530
def remove_monitoring(self, category):
531
"""Remove specific category of event handlers."""
532
if category in self.handlers:
533
self.client.callback.deregister(self.handlers[category])
534
del self.handlers[category]
535
print(f"{category} monitoring disabled")
536
537
def list_active_events(self):
538
"""Show currently registered events."""
539
events = self.client.callback.get()
540
if events:
541
print("Active events:")
542
for event in events:
543
print(f" - {event}")
544
else:
545
print("No events registered")
546
547
def shutdown(self):
548
"""Clean shutdown."""
549
self.client.disconnect()
550
551
# Usage
552
manager = OBSEventManager()
553
554
try:
555
# Start with scene monitoring
556
manager.add_scene_monitoring()
557
manager.list_active_events()
558
559
input("Press Enter to add input monitoring...\n")
560
manager.add_input_monitoring()
561
manager.list_active_events()
562
563
input("Press Enter to remove scene monitoring...\n")
564
manager.remove_monitoring('scenes')
565
manager.list_active_events()
566
567
input("Press Enter to exit...\n")
568
569
finally:
570
manager.shutdown()
571
```