Merge pull request #11 from onyx-and-iris/dev

check if auth required, context manager for reqclient + logging
This commit is contained in:
Adem 2022-10-26 12:39:11 +03:00 committed by GitHub
commit f239173edb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 98 additions and 71 deletions

View File

@ -14,3 +14,5 @@ host = "localhost"
port = 4455 port = 4455
password = "mystrongpass" password = "mystrongpass"
``` ```
Closing OBS ends the script.

View File

@ -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

View File

@ -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",))

View File

@ -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()

View File

@ -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"]

View File

@ -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)

View File

@ -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):

View File

@ -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):
""" """