2022-06-05 12:40:55 +01:00
|
|
|
import base64
|
2022-07-25 23:51:30 +01:00
|
|
|
import hashlib
|
|
|
|
import json
|
2022-07-26 21:46:59 +01:00
|
|
|
import time
|
2022-07-26 04:36:55 +01:00
|
|
|
from enum import IntEnum
|
2022-07-25 23:51:30 +01:00
|
|
|
from pathlib import Path
|
2022-06-05 12:40:55 +01:00
|
|
|
from random import randint
|
|
|
|
|
2022-07-25 23:51:30 +01:00
|
|
|
import tomllib
|
|
|
|
import websocket
|
|
|
|
|
2022-07-26 04:36:55 +01:00
|
|
|
Subs = IntEnum(
|
|
|
|
"Subs",
|
|
|
|
"general config scenes inputs transitions filters outputs sceneitems mediainputs vendors ui",
|
|
|
|
start=0,
|
|
|
|
)
|
|
|
|
|
2022-07-25 23:51:30 +01:00
|
|
|
|
2022-06-05 12:40:55 +01:00
|
|
|
class ObsClient(object):
|
2022-07-26 21:46:59 +01:00
|
|
|
DELAY = 0.001
|
|
|
|
|
2022-07-25 23:51:30 +01:00
|
|
|
def __init__(self, **kwargs):
|
|
|
|
defaultkwargs = {key: None for key in ["host", "port", "password"]}
|
|
|
|
kwargs = defaultkwargs | kwargs
|
|
|
|
for attr, val in kwargs.items():
|
|
|
|
setattr(self, attr, val)
|
|
|
|
if not (self.host and self.port and self.password):
|
|
|
|
conn = self._conn_from_toml()
|
|
|
|
self.host = conn["host"]
|
|
|
|
self.port = conn["port"]
|
|
|
|
self.password = conn["password"]
|
|
|
|
|
2022-06-05 12:40:55 +01:00
|
|
|
self.ws = websocket.WebSocket()
|
|
|
|
self.ws.connect(f"ws://{self.host}:{self.port}")
|
|
|
|
self.server_hello = json.loads(self.ws.recv())
|
|
|
|
|
2022-07-25 23:51:30 +01:00
|
|
|
def _conn_from_toml(self):
|
|
|
|
filepath = Path.cwd() / "config.toml"
|
|
|
|
self._conn = dict()
|
|
|
|
with open(filepath, "rb") as f:
|
|
|
|
self._conn = tomllib.load(f)
|
|
|
|
return self._conn["connection"]
|
|
|
|
|
2022-06-05 12:40:55 +01:00
|
|
|
def authenticate(self):
|
|
|
|
secret = base64.b64encode(
|
|
|
|
hashlib.sha256(
|
2022-07-25 23:51:30 +01:00
|
|
|
(
|
|
|
|
self.password + self.server_hello["d"]["authentication"]["salt"]
|
|
|
|
).encode()
|
|
|
|
).digest()
|
|
|
|
)
|
|
|
|
|
2022-06-05 12:40:55 +01:00
|
|
|
auth = base64.b64encode(
|
2022-07-25 23:51:30 +01:00
|
|
|
hashlib.sha256(
|
|
|
|
(
|
|
|
|
secret.decode()
|
|
|
|
+ self.server_hello["d"]["authentication"]["challenge"]
|
|
|
|
).encode()
|
|
|
|
).digest()
|
|
|
|
).decode()
|
|
|
|
|
2022-07-26 04:36:55 +01:00
|
|
|
all_non_high_volume = (
|
|
|
|
(1 << Subs.general)
|
|
|
|
| (1 << Subs.config)
|
|
|
|
| (1 << Subs.scenes)
|
|
|
|
| (1 << Subs.inputs)
|
|
|
|
| (1 << Subs.transitions)
|
|
|
|
| (1 << Subs.filters)
|
|
|
|
| (1 << Subs.outputs)
|
|
|
|
| (1 << Subs.sceneitems)
|
|
|
|
| (1 << Subs.mediainputs)
|
|
|
|
| (1 << Subs.vendors)
|
|
|
|
| (1 << Subs.ui)
|
|
|
|
)
|
|
|
|
|
|
|
|
payload = {
|
|
|
|
"op": 1,
|
|
|
|
"d": {
|
|
|
|
"rpcVersion": 1,
|
|
|
|
"authentication": auth,
|
2022-07-26 21:46:59 +01:00
|
|
|
"eventSubscriptions": all_non_high_volume,
|
2022-07-26 04:36:55 +01:00
|
|
|
},
|
|
|
|
}
|
2022-07-25 23:51:30 +01:00
|
|
|
|
2022-06-05 12:40:55 +01:00
|
|
|
self.ws.send(json.dumps(payload))
|
|
|
|
return self.ws.recv()
|
|
|
|
|
|
|
|
def req(self, req_type, req_data=None):
|
2022-07-25 23:51:30 +01:00
|
|
|
if req_data:
|
2022-06-05 12:40:55 +01:00
|
|
|
payload = {
|
|
|
|
"op": 6,
|
|
|
|
"d": {
|
|
|
|
"requestType": req_type,
|
2022-07-25 23:51:30 +01:00
|
|
|
"requestId": randint(1, 1000),
|
|
|
|
"requestData": req_data,
|
|
|
|
},
|
2022-06-05 12:40:55 +01:00
|
|
|
}
|
|
|
|
else:
|
|
|
|
payload = {
|
|
|
|
"op": 6,
|
2022-07-25 23:51:30 +01:00
|
|
|
"d": {"requestType": req_type, "requestId": randint(1, 1000)},
|
2022-06-05 12:40:55 +01:00
|
|
|
}
|
|
|
|
self.ws.send(json.dumps(payload))
|
2022-07-26 21:46:59 +01:00
|
|
|
response = json.loads(self.ws.recv())
|
|
|
|
while "requestId" not in response["d"]:
|
|
|
|
response = json.loads(self.ws.recv())
|
|
|
|
time.sleep(self.DELAY)
|
|
|
|
return response["d"]
|