mirror of
https://github.com/onyx-and-iris/obsws-python.git
synced 2024-11-22 12:50:53 +00:00
Merge pull request #3 from onyx-and-iris/main
readme updated. some extra tests.
This commit is contained in:
commit
4b8fec7cb5
90
README.md
90
README.md
@ -1,3 +1,7 @@
|
|||||||
|
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/aatikturk/obsstudio_sdk/blob/main/LICENSE)
|
||||||
|
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
||||||
|
[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
|
||||||
|
|
||||||
# A Python SDK for OBS Studio WebSocket v5.0
|
# A Python SDK for OBS Studio WebSocket v5.0
|
||||||
|
|
||||||
This is a wrapper around OBS Websocket.
|
This is a wrapper around OBS Websocket.
|
||||||
@ -36,7 +40,7 @@ Import and start using, keyword arguments are as follows:
|
|||||||
- `port`: port to access server
|
- `port`: port to access server
|
||||||
- `password`: obs websocket server password
|
- `password`: obs websocket server password
|
||||||
|
|
||||||
Example `__main__.py`
|
Example `__main__.py`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import obsstudio_sdk as obs
|
import obsstudio_sdk as obs
|
||||||
@ -48,6 +52,90 @@ cl = obs.ReqClient(host='localhost', port=4455, password='mystrongpass')
|
|||||||
cl.toggle_input_mute('Mic/Aux')
|
cl.toggle_input_mute('Mic/Aux')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Requests
|
||||||
|
|
||||||
|
Method names for requests match the API calls but snake cased.
|
||||||
|
|
||||||
|
example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
cl = ReqClient()
|
||||||
|
|
||||||
|
# GetVersion
|
||||||
|
resp = cl.get_version()
|
||||||
|
|
||||||
|
# SetCurrentProgramScene
|
||||||
|
cl.set_current_program_scene()
|
||||||
|
```
|
||||||
|
|
||||||
|
For a full list of requests refer to [Requests](https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#requests)
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
When registering a function callback use the name of the expected API event in snake case form.
|
||||||
|
|
||||||
|
example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
cl = EventClient()
|
||||||
|
|
||||||
|
def scene_created(data):
|
||||||
|
...
|
||||||
|
|
||||||
|
# SceneCreated
|
||||||
|
cl.callback.register(scene_created)
|
||||||
|
|
||||||
|
def input_mute_state_changed(data):
|
||||||
|
...
|
||||||
|
|
||||||
|
# InputMuteStateChanged
|
||||||
|
cl.callback.register(input_mute_state_changed)
|
||||||
|
|
||||||
|
# returns a list of currently registered events
|
||||||
|
print(cl.callback.get())
|
||||||
|
|
||||||
|
# You may also deregister a callback
|
||||||
|
cl.callback.deregister(input_mute_state_changed)
|
||||||
|
```
|
||||||
|
|
||||||
|
`register(fns)` and `deregister(fns)` accept both single functions and lists of functions.
|
||||||
|
|
||||||
|
For a full list of events refer to [Events](https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#events)
|
||||||
|
|
||||||
|
### Attributes
|
||||||
|
|
||||||
|
For both request responses and event data you may inspect the available attributes using `attrs()`.
|
||||||
|
|
||||||
|
example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
resp = cl.get_version()
|
||||||
|
print(resp.attrs())
|
||||||
|
|
||||||
|
def scene_created(data):
|
||||||
|
print(data.attrs())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Errors
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
First install development dependencies:
|
||||||
|
|
||||||
|
`pip install -e .['dev']`
|
||||||
|
|
||||||
|
To run all tests:
|
||||||
|
|
||||||
|
```
|
||||||
|
pytest -v
|
||||||
|
```
|
||||||
|
|
||||||
### Official Documentation
|
### Official Documentation
|
||||||
|
|
||||||
|
For the full documentation:
|
||||||
|
|
||||||
- [OBS Websocket SDK](https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#obs-websocket-501-protocol)
|
- [OBS Websocket SDK](https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#obs-websocket-501-protocol)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from .events import EventClient
|
from .events import EventClient
|
||||||
from .reqs import ReqClient
|
from .reqs import ReqClient
|
||||||
|
|
||||||
__ALL__ = ["ReqClient", "EventsClient"]
|
__ALL__ = ["ReqClient", "EventClient"]
|
||||||
|
@ -17,7 +17,7 @@ class Callback:
|
|||||||
return [to_camel_case(fn.__name__[2:]) for fn in self._callbacks]
|
return [to_camel_case(fn.__name__[2:]) for fn in self._callbacks]
|
||||||
|
|
||||||
def trigger(self, event, data):
|
def trigger(self, event, data):
|
||||||
"""trigger callback on update"""
|
"""trigger callback on event"""
|
||||||
|
|
||||||
for fn in self._callbacks:
|
for fn in self._callbacks:
|
||||||
if fn.__name__ == f"on_{to_snake_case(event)}":
|
if fn.__name__ == f"on_{to_snake_case(event)}":
|
||||||
@ -36,7 +36,7 @@ class Callback:
|
|||||||
self._callbacks.append(fns)
|
self._callbacks.append(fns)
|
||||||
|
|
||||||
def deregister(self, fns: Union[Iterable, Callable]):
|
def deregister(self, fns: Union[Iterable, Callable]):
|
||||||
"""deregisters a callback from _callbacks"""
|
"""deregisters callback functions"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
iterator = iter(fns)
|
iterator = iter(fns)
|
||||||
|
@ -592,7 +592,7 @@ class ReqClient:
|
|||||||
}
|
}
|
||||||
self.send("SetSceneSceneTransitionOverride", payload)
|
self.send("SetSceneSceneTransitionOverride", payload)
|
||||||
|
|
||||||
def get_input_list(self, kind):
|
def get_input_list(self, kind=None):
|
||||||
"""
|
"""
|
||||||
Gets a list of all inputs in OBS.
|
Gets a list of all inputs in OBS.
|
||||||
|
|
||||||
|
41
setup.py
41
setup.py
@ -1,30 +1,40 @@
|
|||||||
import pathlib
|
import pathlib
|
||||||
from setuptools import setup, find_packages
|
|
||||||
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
HERE = pathlib.Path(__file__).parent
|
HERE = pathlib.Path(__file__).parent
|
||||||
|
|
||||||
|
|
||||||
VERSION = '1.0.1'
|
VERSION = "1.0.1"
|
||||||
PACKAGE_NAME = 'obsstudio_sdk'
|
PACKAGE_NAME = "obsstudio_sdk"
|
||||||
AUTHOR = 'Adem Atikturk'
|
AUTHOR = "Adem Atikturk"
|
||||||
AUTHOR_EMAIL = 'aatikturk@gmail.com'
|
AUTHOR_EMAIL = "aatikturk@gmail.com"
|
||||||
URL = 'https://github.com/aatikturk/obsstudio_sdk'
|
URL = "https://github.com/aatikturk/obsstudio_sdk"
|
||||||
LICENSE = 'GNU General Public License v3.0'
|
LICENSE = "GNU General Public License v3.0"
|
||||||
DESCRIPTION = 'A Python SDK for OBS Studio WebSocket v5.0'
|
DESCRIPTION = "A Python SDK for OBS Studio WebSocket v5.0"
|
||||||
|
|
||||||
|
|
||||||
LONG_DESCRIPTION = (HERE / "README.md").read_text()
|
LONG_DESCRIPTION = (HERE / "README.md").read_text()
|
||||||
LONG_DESC_TYPE = "text/markdown"
|
LONG_DESC_TYPE = "text/markdown"
|
||||||
|
|
||||||
# Dependencies for the package
|
# Dependencies for the package
|
||||||
INSTALL_REQUIRES = [
|
INSTALL_REQUIRES = ["websocket-client"]
|
||||||
'websocket-client'
|
|
||||||
]
|
# Development dependencies
|
||||||
|
EXTRAS_REQUIRE = {
|
||||||
|
"dev": [
|
||||||
|
"pytest",
|
||||||
|
"pytest-randomly",
|
||||||
|
"black",
|
||||||
|
"isort",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
# Python version requirement
|
# Python version requirement
|
||||||
PYTHON_REQUIRES = '>=3.11'
|
PYTHON_REQUIRES = ">=3.11"
|
||||||
|
|
||||||
setup(name=PACKAGE_NAME,
|
setup(
|
||||||
|
name=PACKAGE_NAME,
|
||||||
version=VERSION,
|
version=VERSION,
|
||||||
description=DESCRIPTION,
|
description=DESCRIPTION,
|
||||||
long_description=LONG_DESCRIPTION,
|
long_description=LONG_DESCRIPTION,
|
||||||
@ -34,6 +44,7 @@ setup(name=PACKAGE_NAME,
|
|||||||
author_email=AUTHOR_EMAIL,
|
author_email=AUTHOR_EMAIL,
|
||||||
url=URL,
|
url=URL,
|
||||||
install_requires=INSTALL_REQUIRES,
|
install_requires=INSTALL_REQUIRES,
|
||||||
|
extras_require=EXTRAS_REQUIRE,
|
||||||
python_requires=PYTHON_REQUIRES,
|
python_requires=PYTHON_REQUIRES,
|
||||||
packages=find_packages()
|
packages=find_packages(),
|
||||||
)
|
)
|
||||||
|
@ -4,8 +4,13 @@ req_cl = obs.ReqClient()
|
|||||||
|
|
||||||
|
|
||||||
def setup_module():
|
def setup_module():
|
||||||
pass
|
req_cl.create_scene("START_TEST")
|
||||||
|
req_cl.create_scene("BRB_TEST")
|
||||||
|
req_cl.create_scene("END_TEST")
|
||||||
|
|
||||||
|
|
||||||
def teardown_module():
|
def teardown_module():
|
||||||
|
req_cl.remove_scene("START_TEST")
|
||||||
|
req_cl.remove_scene("BRB_TEST")
|
||||||
|
req_cl.remove_scene("END_TEST")
|
||||||
req_cl.base_client.ws.close()
|
req_cl.base_client.ws.close()
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from tests import req_cl
|
from tests import req_cl
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,34 +11,13 @@ class TestRequests:
|
|||||||
assert hasattr(resp, "obs_version")
|
assert hasattr(resp, "obs_version")
|
||||||
assert hasattr(resp, "obs_web_socket_version")
|
assert hasattr(resp, "obs_web_socket_version")
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"scene",
|
|
||||||
[
|
|
||||||
("START"),
|
|
||||||
("BRB"),
|
|
||||||
("END"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_current_program_scene(self, scene):
|
|
||||||
req_cl.set_current_program_scene(scene)
|
|
||||||
resp = req_cl.get_current_program_scene()
|
|
||||||
assert resp.current_program_scene_name == scene
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"state",
|
|
||||||
[
|
|
||||||
(False),
|
|
||||||
(True),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_studio_mode_enabled(self, state):
|
|
||||||
req_cl.set_studio_mode_enabled(state)
|
|
||||||
resp = req_cl.get_studio_mode_enabled()
|
|
||||||
assert resp.studio_mode_enabled == state
|
|
||||||
|
|
||||||
def test_get_hot_key_list(self):
|
def test_get_hot_key_list(self):
|
||||||
resp = req_cl.get_hot_key_list()
|
resp = req_cl.get_hot_key_list()
|
||||||
hotkey_list = [
|
obsbasic_hotkey_list = [
|
||||||
|
"OBSBasic.SelectScene",
|
||||||
|
"OBSBasic.SelectScene",
|
||||||
|
"OBSBasic.SelectScene",
|
||||||
|
"OBSBasic.SelectScene",
|
||||||
"OBSBasic.StartStreaming",
|
"OBSBasic.StartStreaming",
|
||||||
"OBSBasic.StopStreaming",
|
"OBSBasic.StopStreaming",
|
||||||
"OBSBasic.ForceStopStreaming",
|
"OBSBasic.ForceStopStreaming",
|
||||||
@ -59,29 +38,8 @@ class TestRequests:
|
|||||||
"OBSBasic.ResetStats",
|
"OBSBasic.ResetStats",
|
||||||
"OBSBasic.Screenshot",
|
"OBSBasic.Screenshot",
|
||||||
"OBSBasic.SelectedSourceScreenshot",
|
"OBSBasic.SelectedSourceScreenshot",
|
||||||
"libobs.mute",
|
|
||||||
"libobs.unmute",
|
|
||||||
"libobs.push-to-mute",
|
|
||||||
"libobs.push-to-talk",
|
|
||||||
"libobs.mute",
|
|
||||||
"libobs.unmute",
|
|
||||||
"libobs.push-to-mute",
|
|
||||||
"libobs.push-to-talk",
|
|
||||||
"OBSBasic.SelectScene",
|
|
||||||
"OBSBasic.SelectScene",
|
|
||||||
"OBSBasic.SelectScene",
|
|
||||||
"OBSBasic.SelectScene",
|
|
||||||
"libobs.show_scene_item.Colour Source 2",
|
|
||||||
"libobs.hide_scene_item.Colour Source 2",
|
|
||||||
"libobs.show_scene_item.Colour Source 3",
|
|
||||||
"libobs.hide_scene_item.Colour Source 3",
|
|
||||||
"libobs.show_scene_item.Colour Source",
|
|
||||||
"libobs.hide_scene_item.Colour Source",
|
|
||||||
"OBSBasic.QuickTransition.1",
|
|
||||||
"OBSBasic.QuickTransition.2",
|
|
||||||
"OBSBasic.QuickTransition.3",
|
|
||||||
]
|
]
|
||||||
assert all(x in resp.hotkeys for x in hotkey_list)
|
assert all(x in resp.hotkeys for x in obsbasic_hotkey_list)
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"name,data",
|
"name,data",
|
||||||
@ -103,9 +61,53 @@ class TestRequests:
|
|||||||
resp = req_cl.get_profile_list()
|
resp = req_cl.get_profile_list()
|
||||||
assert "test" not in resp.profiles
|
assert "test" not in resp.profiles
|
||||||
|
|
||||||
|
def test_stream_service_settings(self):
|
||||||
|
settings = {
|
||||||
|
"server": "rtmp://addressofrtmpserver",
|
||||||
|
"key": "live_myvery_secretkey",
|
||||||
|
}
|
||||||
|
req_cl.set_stream_service_settings(
|
||||||
|
"rtmp_common",
|
||||||
|
settings,
|
||||||
|
)
|
||||||
|
resp = req_cl.get_stream_service_settings()
|
||||||
|
assert resp.stream_service_type == "rtmp_common"
|
||||||
|
assert resp.stream_service_settings == {
|
||||||
|
"server": "rtmp://addressofrtmpserver",
|
||||||
|
"key": "live_myvery_secretkey",
|
||||||
|
}
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"scene",
|
||||||
|
[
|
||||||
|
("START_TEST"),
|
||||||
|
("BRB_TEST"),
|
||||||
|
("END_TEST"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_current_program_scene(self, scene):
|
||||||
|
req_cl.set_current_program_scene(scene)
|
||||||
|
resp = req_cl.get_current_program_scene()
|
||||||
|
assert resp.current_program_scene_name == scene
|
||||||
|
|
||||||
|
def test_input_list(self):
|
||||||
|
req_cl.create_input(
|
||||||
|
"START_TEST", "test", "color_source_v3", {"color": 4294945535}, True
|
||||||
|
)
|
||||||
|
resp = req_cl.get_input_list()
|
||||||
|
assert {
|
||||||
|
"inputKind": "color_source_v3",
|
||||||
|
"inputName": "test",
|
||||||
|
"unversionedInputKind": "color_source",
|
||||||
|
} in resp.inputs
|
||||||
|
resp = req_cl.get_input_settings("test")
|
||||||
|
assert resp.input_kind == "color_source_v3"
|
||||||
|
assert resp.input_settings == {"color": 4294945535}
|
||||||
|
req_cl.remove_input("test")
|
||||||
|
|
||||||
def test_source_filter(self):
|
def test_source_filter(self):
|
||||||
req_cl.create_source_filter("START", "test", "color_key_filter_v2")
|
req_cl.create_source_filter("START_TEST", "test", "color_key_filter_v2")
|
||||||
resp = req_cl.get_source_filter_list("START")
|
resp = req_cl.get_source_filter_list("START_TEST")
|
||||||
assert resp.filters == [
|
assert resp.filters == [
|
||||||
{
|
{
|
||||||
"filterEnabled": True,
|
"filterEnabled": True,
|
||||||
@ -115,4 +117,16 @@ class TestRequests:
|
|||||||
"filterSettings": {},
|
"filterSettings": {},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
req_cl.remove_source_filter("START", "test")
|
req_cl.remove_source_filter("START_TEST", "test")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"state",
|
||||||
|
[
|
||||||
|
(False),
|
||||||
|
(True),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_studio_mode_enabled(self, state):
|
||||||
|
req_cl.set_studio_mode_enabled(state)
|
||||||
|
resp = req_cl.get_studio_mode_enabled()
|
||||||
|
assert resp.studio_mode_enabled == state
|
||||||
|
Loading…
Reference in New Issue
Block a user