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

add values to defaultkwargs, fix event names in readme + other minor additions
This commit is contained in:
Adem 2022-11-20 21:15:12 +03:00 committed by GitHub
commit ce6873f57a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 35 deletions

View File

@ -12,7 +12,7 @@ Not all endpoints in the official documentation are implemented.
- [OBS Studio](https://obsproject.com/) - [OBS Studio](https://obsproject.com/)
- [OBS Websocket v5 Plugin](https://github.com/obsproject/obs-websocket/releases/tag/5.0.0) - [OBS Websocket v5 Plugin](https://github.com/obsproject/obs-websocket/releases/tag/5.0.0)
- With the release of OBS Studio version 28, Websocket plugin is included by default. But it should be manually installed for earlier versions of OBS. - With the release of OBS Studio version 28, Websocket plugin is included by default. But it should be manually installed for earlier versions of OBS.
- Python 3.10 or greater - Python 3.10 or greater
### How to install using pip ### How to install using pip
@ -23,7 +23,19 @@ pip install obsws-python
### How to Use ### How to Use
Load connection info from toml config. A valid `config.toml` might look like this: By default the clients connect with parameters:
- `host`: "localhost"
- `port`: 4455
- `password`: None
You may override these parameters by storing them in a toml config file or passing them as keyword arguments.
Order of precedence: keyword arguments then config file then default values.
#### `config file`
A valid `config.toml` might look like this:
```toml ```toml
[connection] [connection]
@ -36,12 +48,6 @@ It should be placed next to your `__main__.py` file.
#### Otherwise: #### Otherwise:
Import and start using, keyword arguments are as follows:
- `host`: obs websocket server
- `port`: port to access server
- `password`: obs websocket server password
Example `__main__.py`: Example `__main__.py`:
```python ```python
@ -75,7 +81,7 @@ For a full list of requests refer to [Requests](https://github.com/obsproject/ob
### Events ### Events
When registering a function callback use the name of the expected API event in snake case form. When registering a callback function use the name of the expected API event in snake case form, prepended with "on\_".
example: example:
@ -83,23 +89,23 @@ example:
# load conn info from config.toml # load conn info from config.toml
cl = obs.EventClient() cl = obs.EventClient()
def scene_created(data): def on_scene_created(data):
... ...
# SceneCreated # SceneCreated
cl.callback.register(scene_created) cl.callback.register(on_scene_created)
def input_mute_state_changed(data): def on_input_mute_state_changed(data):
... ...
# InputMuteStateChanged # InputMuteStateChanged
cl.callback.register(input_mute_state_changed) cl.callback.register(on_input_mute_state_changed)
# returns a list of currently registered events # returns a list of currently registered events
print(cl.callback.get()) print(cl.callback.get())
# You may also deregister a callback # You may also deregister a callback
cl.callback.deregister(input_mute_state_changed) cl.callback.deregister(on_input_mute_state_changed)
``` ```
`register(fns)` and `deregister(fns)` accept both single functions and lists of functions. `register(fns)` and `deregister(fns)` accept both single functions and lists of functions.
@ -116,7 +122,7 @@ example:
resp = cl.get_version() resp = cl.get_version()
print(resp.attrs()) print(resp.attrs())
def scene_created(data): def on_scene_created(data):
print(data.attrs()) print(data.attrs())
``` ```
@ -126,6 +132,21 @@ If a request fails an `OBSSDKError` will be raised with a status code.
For a full list of status codes refer to [Codes](https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#requeststatus) For a full list of status codes refer to [Codes](https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#requeststatus)
### Logging
If you want to see the raw messages simply set log level to DEBUG
example:
```python
import obsws_python as obs
import logging
logging.basicConfig(level=logging.DEBUG)
...
```
### Tests ### Tests
First install development dependencies: First install development dependencies:

View File

@ -1,6 +1,6 @@
from .version import version as __version__
from .enum import Subs from .enum import Subs
from .events import EventClient from .events import EventClient
from .reqs import ReqClient from .reqs import ReqClient
from .version import version as __version__
__ALL__ = ["ReqClient", "EventClient", "Subs"] __ALL__ = ["ReqClient", "EventClient", "Subs"]

View File

@ -19,28 +19,30 @@ class ObsClient:
logger = logging.getLogger("baseclient.obsclient") logger = logging.getLogger("baseclient.obsclient")
def __init__(self, **kwargs): def __init__(self, **kwargs):
defaultkwargs = { defaultkwargs = {"host": "localhost", "port": 4455, "password": None, "subs": 0}
**{key: None for key in ["host", "port", "password"]}, if not any(key in kwargs for key in ("host", "port", "password")):
"subs": 0, kwargs |= self._conn_from_toml()
}
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):
conn = self._conn_from_toml() self.logger.info(
self.host = conn["host"] "Connecting with parameters: {host} {port} {password} {subs}".format(
self.port = conn["port"] **self.__dict__
self.password = conn["password"] )
)
self.ws = websocket.WebSocket() self.ws = websocket.WebSocket()
self.ws.connect(f"ws://{self.host}:{self.port}") self.ws.connect(f"ws://{self.host}:{self.port}")
self.server_hello = json.loads(self.ws.recv()) self.server_hello = json.loads(self.ws.recv())
def _conn_from_toml(self): def _conn_from_toml(self) -> dict:
conn = {}
filepath = Path.cwd() / "config.toml" filepath = Path.cwd() / "config.toml"
with open(filepath, "rb") as f: if filepath.exists():
conn = tomllib.load(f) with open(filepath, "rb") as f:
return conn["connection"] conn = tomllib.load(f)
return conn["connection"] if "connection" in conn else conn
def authenticate(self): def authenticate(self):
payload = { payload = {
@ -52,6 +54,8 @@ class ObsClient:
} }
if "authentication" in self.server_hello["d"]: if "authentication" in self.server_hello["d"]:
if not self.password:
raise OBSSDKError("authentication enabled but no password provided")
secret = base64.b64encode( secret = base64.b64encode(
hashlib.sha256( hashlib.sha256(
( (
@ -79,14 +83,13 @@ class ObsClient:
raise OBSSDKError("failed to identify client with the server") 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 = { payload = {
"op": 6, "op": 6,
"d": {"requestType": req_type, "requestId": id}, "d": {"requestType": req_type, "requestId": randint(1, 1000)},
} }
if req_data: if req_data:
payload["d"]["requestData"] = req_data payload["d"]["requestData"] = req_data
self.logger.debug(f"Sending request {payload}")
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}") self.logger.debug(f"Response received {response}")

View File

@ -75,7 +75,7 @@ class ReqClient:
""" """
self.send("BroadcastCustomEvent", eventData) self.send("BroadcastCustomEvent", eventData)
def call_vendor_request(self, vendorName, requestType, requestData=None): def call_vendor_request(self, vendor_name, request_type, request_data=None):
""" """
Call a request registered to a vendor. Call a request registered to a vendor.
@ -97,7 +97,10 @@ class ReqClient:
""" """
return self.send(requestType, requestData) payload = {"vendorName": vendor_name, "requestType": request_type}
if request_data:
payload["requestData"] = request_data
return self.send("CallVendorRequest", payload)
def get_hot_key_list(self): def get_hot_key_list(self):
""" """
@ -110,6 +113,8 @@ class ReqClient:
""" """
return self.send("GetHotkeyList") return self.send("GetHotkeyList")
get_hotkey_list = get_hot_key_list
def trigger_hot_key_by_name(self, hotkeyName): def trigger_hot_key_by_name(self, hotkeyName):
""" """
Triggers a hotkey using its name. For hotkey names Triggers a hotkey using its name. For hotkey names
@ -123,6 +128,8 @@ class ReqClient:
payload = {"hotkeyName": hotkeyName} payload = {"hotkeyName": hotkeyName}
self.send("TriggerHotkeyByName", payload) self.send("TriggerHotkeyByName", payload)
trigger_hotkey_by_name = trigger_hot_key_by_name
def trigger_hot_key_by_key_sequence( def trigger_hot_key_by_key_sequence(
self, keyId, pressShift, pressCtrl, pressAlt, pressCmd self, keyId, pressShift, pressCtrl, pressAlt, pressCmd
): ):
@ -155,6 +162,8 @@ class ReqClient:
} }
self.send("TriggerHotkeyByKeySequence", payload) self.send("TriggerHotkeyByKeySequence", payload)
trigger_hotkey_by_key_sequence = trigger_hot_key_by_key_sequence
def sleep(self, sleepMillis=None, sleepFrames=None): def sleep(self, sleepMillis=None, sleepFrames=None):
""" """
Sleeps for a time duration or number of frames. Sleeps for a time duration or number of frames.

View File

@ -1 +1 @@
version = "1.2.0" version = "1.3.0"