mirror of
				https://github.com/onyx-and-iris/obsws-python.git
				synced 2025-10-31 04:41:52 +00:00 
			
		
		
		
	Merge pull request #11 from onyx-and-iris/dev
check if auth required, context manager for reqclient + logging
This commit is contained in:
		
						commit
						f239173edb
					
				| @ -14,3 +14,5 @@ host = "localhost" | |||||||
| port = 4455 | port = 4455 | ||||||
| password = "mystrongpass" | password = "mystrongpass" | ||||||
| ``` | ``` | ||||||
|  | 
 | ||||||
|  | Closing OBS ends the script. | ||||||
|  | |||||||
| @ -1,10 +1,12 @@ | |||||||
|  | import time | ||||||
|  | 
 | ||||||
| import obsws_python as obs | import obsws_python as obs | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Observer: | class Observer: | ||||||
|     def __init__(self, cl): |     def __init__(self): | ||||||
|         self._cl = cl |         self._client = obs.EventClient() | ||||||
|         self._cl.callback.register( |         self._client.callback.register( | ||||||
|             [ |             [ | ||||||
|                 self.on_current_program_scene_changed, |                 self.on_current_program_scene_changed, | ||||||
|                 self.on_scene_created, |                 self.on_scene_created, | ||||||
| @ -12,7 +14,8 @@ class Observer: | |||||||
|                 self.on_exit_started, |                 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): |     def on_current_program_scene_changed(self, data): | ||||||
|         """The current program scene has changed.""" |         """The current program scene has changed.""" | ||||||
| @ -29,13 +32,12 @@ class Observer: | |||||||
|     def on_exit_started(self, _): |     def on_exit_started(self, _): | ||||||
|         """OBS has begun the shutdown process.""" |         """OBS has begun the shutdown process.""" | ||||||
|         print(f"OBS closing!") |         print(f"OBS closing!") | ||||||
|         self._cl.unsubscribe() |         self._client.unsubscribe() | ||||||
|  |         self.running = False | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     cl = obs.EventClient() |     observer = Observer() | ||||||
|     observer = Observer(cl) |  | ||||||
| 
 | 
 | ||||||
|     while cmd := input("<Enter> to exit\n"): |     while observer.running: | ||||||
|         if not cmd: |         time.sleep(0.1) | ||||||
|             break |  | ||||||
|  | |||||||
| @ -5,10 +5,10 @@ import obsws_python as obs | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Observer: | class Observer: | ||||||
|     def __init__(self, cl): |     def __init__(self): | ||||||
|         self._cl = cl |         self._client = obs.EventClient() | ||||||
|         self._cl.callback.register(self.on_current_program_scene_changed) |         self._client.callback.register(self.on_current_program_scene_changed) | ||||||
|         print(f"Registered events: {self._cl.callback.get()}") |         print(f"Registered events: {self._client.callback.get()}") | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def event_identifier(self): |     def event_identifier(self): | ||||||
| @ -20,20 +20,19 @@ class Observer: | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def version(): | def version(): | ||||||
|     resp = req_cl.get_version() |     resp = req_client.get_version() | ||||||
|     print( |     print( | ||||||
|         f"Running OBS version:{resp.obs_version} with websocket version:{resp.obs_web_socket_version}" |         f"Running OBS version:{resp.obs_version} with websocket version:{resp.obs_web_socket_version}" | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def set_scene(scene, *args): | def set_scene(scene, *args): | ||||||
|     req_cl.set_current_program_scene(scene) |     req_client.set_current_program_scene(scene) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     req_cl = obs.ReqClient() |     req_client = obs.ReqClient() | ||||||
|     ev_cl = obs.EventClient() |     observer = Observer() | ||||||
|     observer = Observer(ev_cl) |  | ||||||
| 
 | 
 | ||||||
|     keyboard.add_hotkey("0", version) |     keyboard.add_hotkey("0", version) | ||||||
|     keyboard.add_hotkey("1", set_scene, args=("START",)) |     keyboard.add_hotkey("1", set_scene, args=("START",)) | ||||||
|  | |||||||
| @ -4,16 +4,15 @@ import obsws_python as obs | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def main(): | def main(): | ||||||
|     resp = cl.get_scene_list() |     with obs.ReqClient() as client: | ||||||
|     scenes = reversed(tuple(di.get("sceneName") for di in resp.scenes)) |         resp = client.get_scene_list() | ||||||
|  |         scenes = [di.get("sceneName") for di in reversed(resp.scenes)] | ||||||
| 
 | 
 | ||||||
|     for scene in scenes: |         for scene in scenes: | ||||||
|         print(f"Switching to scene {scene}") |             print(f"Switching to scene {scene}") | ||||||
|         cl.set_current_program_scene(scene) |             client.set_current_program_scene(scene) | ||||||
|         time.sleep(0.5) |             time.sleep(0.5) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     cl = obs.ReqClient() |  | ||||||
| 
 |  | ||||||
|     main() |     main() | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import base64 | import base64 | ||||||
| import hashlib | import hashlib | ||||||
| import json | import json | ||||||
|  | import logging | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from random import randint | from random import randint | ||||||
| 
 | 
 | ||||||
| @ -11,9 +12,11 @@ except ModuleNotFoundError: | |||||||
| 
 | 
 | ||||||
| import websocket | import websocket | ||||||
| 
 | 
 | ||||||
|  | from .error import OBSSDKError | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class ObsClient: | class ObsClient: | ||||||
|     DELAY = 0.001 |     logger = logging.getLogger("baseclient.obsclient") | ||||||
| 
 | 
 | ||||||
|     def __init__(self, **kwargs): |     def __init__(self, **kwargs): | ||||||
|         defaultkwargs = { |         defaultkwargs = { | ||||||
| @ -23,7 +26,7 @@ class ObsClient: | |||||||
|         kwargs = defaultkwargs | kwargs |         kwargs = defaultkwargs | kwargs | ||||||
|         for attr, val in kwargs.items(): |         for attr, val in kwargs.items(): | ||||||
|             setattr(self, attr, val) |             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() |             conn = self._conn_from_toml() | ||||||
|             self.host = conn["host"] |             self.host = conn["host"] | ||||||
|             self.port = conn["port"] |             self.port = conn["port"] | ||||||
| @ -40,50 +43,51 @@ class ObsClient: | |||||||
|         return conn["connection"] |         return conn["connection"] | ||||||
| 
 | 
 | ||||||
|     def authenticate(self): |     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 = { |         payload = { | ||||||
|             "op": 1, |             "op": 1, | ||||||
|             "d": { |             "d": { | ||||||
|                 "rpcVersion": 1, |                 "rpcVersion": 1, | ||||||
|                 "authentication": auth, |  | ||||||
|                 "eventSubscriptions": self.subs, |                 "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)) |         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): |     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: |         if req_data: | ||||||
|             payload = { |             payload["d"]["requestData"] = req_data | ||||||
|                 "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)}, |  | ||||||
|             } |  | ||||||
|         self.ws.send(json.dumps(payload)) |         self.ws.send(json.dumps(payload)) | ||||||
|         response = json.loads(self.ws.recv()) |         response = json.loads(self.ws.recv()) | ||||||
|  |         self.logger.debug(f"Response received {response}") | ||||||
|         return response["d"] |         return response["d"] | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ class Callback: | |||||||
|             for fn in iterator: |             for fn in iterator: | ||||||
|                 if fn not in self._callbacks: |                 if fn not in self._callbacks: | ||||||
|                     self._callbacks.append(fn) |                     self._callbacks.append(fn) | ||||||
|         except TypeError as e: |         except TypeError: | ||||||
|             if fns not in self._callbacks: |             if fns not in self._callbacks: | ||||||
|                 self._callbacks.append(fns) |                 self._callbacks.append(fns) | ||||||
| 
 | 
 | ||||||
| @ -43,7 +43,7 @@ class Callback: | |||||||
|             for fn in iterator: |             for fn in iterator: | ||||||
|                 if fn in self._callbacks: |                 if fn in self._callbacks: | ||||||
|                     self._callbacks.remove(fn) |                     self._callbacks.remove(fn) | ||||||
|         except TypeError as e: |         except TypeError: | ||||||
|             if fns in self._callbacks: |             if fns in self._callbacks: | ||||||
|                 self._callbacks.remove(fns) |                 self._callbacks.remove(fns) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import json | import json | ||||||
|  | import logging | ||||||
| import time | import time | ||||||
| from enum import IntEnum | from enum import IntEnum | ||||||
| from threading import Thread | from threading import Thread | ||||||
| @ -20,6 +21,7 @@ Subs = IntEnum( | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class EventClient: | class EventClient: | ||||||
|  |     logger = logging.getLogger("events.eventclient") | ||||||
|     DELAY = 0.001 |     DELAY = 0.001 | ||||||
| 
 | 
 | ||||||
|     def __init__(self, **kwargs): |     def __init__(self, **kwargs): | ||||||
| @ -40,10 +42,14 @@ class EventClient: | |||||||
|         } |         } | ||||||
|         kwargs = defaultkwargs | kwargs |         kwargs = defaultkwargs | kwargs | ||||||
|         self.base_client = ObsClient(**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.callback = Callback() | ||||||
|         self.subscribe() |         self.subscribe() | ||||||
| 
 | 
 | ||||||
|  |     def __repr__(self): | ||||||
|  |         return type(self).__name__ | ||||||
|  | 
 | ||||||
|     def subscribe(self): |     def subscribe(self): | ||||||
|         worker = Thread(target=self.trigger, daemon=True) |         worker = Thread(target=self.trigger, daemon=True) | ||||||
|         worker.start() |         worker.start() | ||||||
| @ -56,12 +62,13 @@ class EventClient: | |||||||
|         """ |         """ | ||||||
|         self.running = True |         self.running = True | ||||||
|         while self.running: |         while self.running: | ||||||
|             self.data = json.loads(self.base_client.ws.recv()) |             event = json.loads(self.base_client.ws.recv()) | ||||||
|             event, data = ( |             self.logger.debug(f"Event received {event}") | ||||||
|                 self.data["d"].get("eventType"), |             type_, data = ( | ||||||
|                 self.data["d"].get("eventData"), |                 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) |             time.sleep(self.DELAY) | ||||||
| 
 | 
 | ||||||
|     def unsubscribe(self): |     def unsubscribe(self): | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | import logging | ||||||
|  | 
 | ||||||
| from .baseclient import ObsClient | from .baseclient import ObsClient | ||||||
| from .error import OBSSDKError | from .error import OBSSDKError | ||||||
| from .util import as_dataclass | from .util import as_dataclass | ||||||
| @ -10,9 +12,21 @@ https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol. | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ReqClient: | class ReqClient: | ||||||
|  |     logger = logging.getLogger("reqs.reqclient") | ||||||
|  | 
 | ||||||
|     def __init__(self, **kwargs): |     def __init__(self, **kwargs): | ||||||
|         self.base_client = ObsClient(**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): |     def send(self, param, data=None): | ||||||
|         response = self.base_client.req(param, data) |         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): |     def get_current_program_scene(self): | ||||||
|         """ |         """ | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user