diff --git a/examples/events/README.md b/examples/events/README.md index ce1b83a..b9f450d 100644 --- a/examples/events/README.md +++ b/examples/events/README.md @@ -14,3 +14,5 @@ host = "localhost" port = 4455 password = "mystrongpass" ``` + +Closing OBS ends the script. diff --git a/examples/events/__main__.py b/examples/events/__main__.py index 6a2ac45..db2bf37 100644 --- a/examples/events/__main__.py +++ b/examples/events/__main__.py @@ -1,10 +1,12 @@ +import time + import obsws_python as obs class Observer: - def __init__(self, cl): - self._cl = cl - self._cl.callback.register( + def __init__(self): + self._client = obs.EventClient() + self._client.callback.register( [ self.on_current_program_scene_changed, self.on_scene_created, @@ -12,7 +14,8 @@ class Observer: self.on_exit_started, ] ) - print(f"Registered events: {self._cl.callback.get()}") + print(f"Registered events: {self._client.callback.get()}") + self.running = True def on_current_program_scene_changed(self, data): """The current program scene has changed.""" @@ -29,13 +32,12 @@ class Observer: def on_exit_started(self, _): """OBS has begun the shutdown process.""" print(f"OBS closing!") - self._cl.unsubscribe() + self._client.unsubscribe() + self.running = False if __name__ == "__main__": - cl = obs.EventClient() - observer = Observer(cl) + observer = Observer() - while cmd := input(" to exit\n"): - if not cmd: - break + while observer.running: + time.sleep(0.1) diff --git a/examples/hotkeys/__main__.py b/examples/hotkeys/__main__.py index 81801a2..e64ca87 100644 --- a/examples/hotkeys/__main__.py +++ b/examples/hotkeys/__main__.py @@ -5,10 +5,10 @@ import obsws_python as obs class Observer: - def __init__(self, cl): - self._cl = cl - self._cl.callback.register(self.on_current_program_scene_changed) - print(f"Registered events: {self._cl.callback.get()}") + def __init__(self): + self._client = obs.EventClient() + self._client.callback.register(self.on_current_program_scene_changed) + print(f"Registered events: {self._client.callback.get()}") @property def event_identifier(self): @@ -20,20 +20,19 @@ class Observer: def version(): - resp = req_cl.get_version() + resp = req_client.get_version() print( f"Running OBS version:{resp.obs_version} with websocket version:{resp.obs_web_socket_version}" ) def set_scene(scene, *args): - req_cl.set_current_program_scene(scene) + req_client.set_current_program_scene(scene) if __name__ == "__main__": - req_cl = obs.ReqClient() - ev_cl = obs.EventClient() - observer = Observer(ev_cl) + req_client = obs.ReqClient() + observer = Observer() keyboard.add_hotkey("0", version) keyboard.add_hotkey("1", set_scene, args=("START",)) diff --git a/examples/scene_rotate/__main__.py b/examples/scene_rotate/__main__.py index 0879e8b..206f150 100644 --- a/examples/scene_rotate/__main__.py +++ b/examples/scene_rotate/__main__.py @@ -4,16 +4,15 @@ import obsws_python as obs def main(): - resp = cl.get_scene_list() - scenes = reversed(tuple(di.get("sceneName") for di in resp.scenes)) + with obs.ReqClient() as client: + resp = client.get_scene_list() + scenes = [di.get("sceneName") for di in reversed(resp.scenes)] - for scene in scenes: - print(f"Switching to scene {scene}") - cl.set_current_program_scene(scene) - time.sleep(0.5) + for scene in scenes: + print(f"Switching to scene {scene}") + client.set_current_program_scene(scene) + time.sleep(0.5) if __name__ == "__main__": - cl = obs.ReqClient() - main() diff --git a/obsws_python/baseclient.py b/obsws_python/baseclient.py index 8361993..fd61da6 100644 --- a/obsws_python/baseclient.py +++ b/obsws_python/baseclient.py @@ -1,6 +1,7 @@ import base64 import hashlib import json +import logging from pathlib import Path from random import randint @@ -11,9 +12,11 @@ except ModuleNotFoundError: import websocket +from .error import OBSSDKError + class ObsClient: - DELAY = 0.001 + logger = logging.getLogger("baseclient.obsclient") def __init__(self, **kwargs): defaultkwargs = { @@ -23,7 +26,7 @@ class ObsClient: kwargs = defaultkwargs | kwargs for attr, val in kwargs.items(): setattr(self, attr, val) - if not (self.host and self.port and self.password): + if not (self.host and self.port): conn = self._conn_from_toml() self.host = conn["host"] self.port = conn["port"] @@ -40,50 +43,51 @@ class ObsClient: return conn["connection"] def authenticate(self): - secret = base64.b64encode( - hashlib.sha256( - ( - self.password + self.server_hello["d"]["authentication"]["salt"] - ).encode() - ).digest() - ) - - auth = base64.b64encode( - hashlib.sha256( - ( - secret.decode() - + self.server_hello["d"]["authentication"]["challenge"] - ).encode() - ).digest() - ).decode() - payload = { "op": 1, "d": { "rpcVersion": 1, - "authentication": auth, "eventSubscriptions": self.subs, }, } + if "authentication" in self.server_hello["d"]: + secret = base64.b64encode( + hashlib.sha256( + ( + self.password + self.server_hello["d"]["authentication"]["salt"] + ).encode() + ).digest() + ) + + auth = base64.b64encode( + hashlib.sha256( + ( + secret.decode() + + self.server_hello["d"]["authentication"]["challenge"] + ).encode() + ).digest() + ).decode() + + payload["d"]["authentication"] = auth + self.ws.send(json.dumps(payload)) - return self.ws.recv() + try: + response = json.loads(self.ws.recv()) + return response["op"] == 2 + except json.decoder.JSONDecodeError: + raise OBSSDKError("failed to identify client with the server") def req(self, req_type, req_data=None): + id = randint(1, 1000) + self.logger.debug(f"Sending request with id {id}") + payload = { + "op": 6, + "d": {"requestType": req_type, "requestId": id}, + } if req_data: - payload = { - "op": 6, - "d": { - "requestType": req_type, - "requestId": randint(1, 1000), - "requestData": req_data, - }, - } - else: - payload = { - "op": 6, - "d": {"requestType": req_type, "requestId": randint(1, 1000)}, - } + payload["d"]["requestData"] = req_data self.ws.send(json.dumps(payload)) response = json.loads(self.ws.recv()) + self.logger.debug(f"Response received {response}") return response["d"] diff --git a/obsws_python/callback.py b/obsws_python/callback.py index 28e090d..20e46fe 100644 --- a/obsws_python/callback.py +++ b/obsws_python/callback.py @@ -31,7 +31,7 @@ class Callback: for fn in iterator: if fn not in self._callbacks: self._callbacks.append(fn) - except TypeError as e: + except TypeError: if fns not in self._callbacks: self._callbacks.append(fns) @@ -43,7 +43,7 @@ class Callback: for fn in iterator: if fn in self._callbacks: self._callbacks.remove(fn) - except TypeError as e: + except TypeError: if fns in self._callbacks: self._callbacks.remove(fns) diff --git a/obsws_python/events.py b/obsws_python/events.py index 56c4b63..a2f07bc 100644 --- a/obsws_python/events.py +++ b/obsws_python/events.py @@ -1,4 +1,5 @@ import json +import logging import time from enum import IntEnum from threading import Thread @@ -20,6 +21,7 @@ Subs = IntEnum( class EventClient: + logger = logging.getLogger("events.eventclient") DELAY = 0.001 def __init__(self, **kwargs): @@ -40,10 +42,14 @@ class EventClient: } kwargs = defaultkwargs | kwargs self.base_client = ObsClient(**kwargs) - self.base_client.authenticate() + if self.base_client.authenticate(): + self.logger.info(f"Successfully identified {self} with the server") self.callback = Callback() self.subscribe() + def __repr__(self): + return type(self).__name__ + def subscribe(self): worker = Thread(target=self.trigger, daemon=True) worker.start() @@ -56,12 +62,13 @@ class EventClient: """ self.running = True while self.running: - self.data = json.loads(self.base_client.ws.recv()) - event, data = ( - self.data["d"].get("eventType"), - self.data["d"].get("eventData"), + event = json.loads(self.base_client.ws.recv()) + self.logger.debug(f"Event received {event}") + type_, data = ( + event["d"].get("eventType"), + event["d"].get("eventData"), ) - self.callback.trigger(event, data if data else {}) + self.callback.trigger(type_, data if data else {}) time.sleep(self.DELAY) def unsubscribe(self): diff --git a/obsws_python/reqs.py b/obsws_python/reqs.py index 95654c8..7f15a35 100644 --- a/obsws_python/reqs.py +++ b/obsws_python/reqs.py @@ -1,3 +1,5 @@ +import logging + from .baseclient import ObsClient from .error import OBSSDKError from .util import as_dataclass @@ -10,9 +12,21 @@ https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol. class ReqClient: + logger = logging.getLogger("reqs.reqclient") + def __init__(self, **kwargs): self.base_client = ObsClient(**kwargs) - self.base_client.authenticate() + if self.base_client.authenticate(): + self.logger.info(f"Successfully identified {self} with the server") + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + self.base_client.ws.close() + + def __repr__(self): + return type(self).__name__ def send(self, param, data=None): response = self.base_client.req(param, data) @@ -486,7 +500,7 @@ class ReqClient: """ - return self.send("GetSceneList") + return self.send("GetGroupList") def get_current_program_scene(self): """