OBS WebSocket RemoteControl

Introduction

What’s this?

obs-ws-rc is a Python 3.5+ library that allows you to establish client connections to the obs-websocket plugin for OBS Studio.

It’s based on asyncio-approach which it inherited from the underlying WebSocket library - websockets

Performing requests

Firstly, obs-websocket‘s protocol provides you with the ability to send requests and retrieve responses to and from OBS Studio.

Let’s see how it’s done with obs-ws-rc:

"""Example shows how to send requests and get responses."""

import asyncio

from obswsrc import OBSWS
from obswsrc.requests import ResponseStatus, StartStreamingRequest
from obswsrc.types import Stream, StreamSettings


async def main():

    async with OBSWS('localhost', 4444, "password") as obsws:

        # We can send an empty StartStreaming request (in that case the plugin
        # will use OBS configuration), but let's provide some settings as well
        stream_settings = StreamSettings(
            server="rtmp://example.org/my_application",
            key="secret_stream_key",
            use_auth=False
        )
        stream = Stream(
            settings=stream_settings,
            type="rtmp_custom",
        )

        # Now let's actually perform a request
        response = await obsws.require(StartStreamingRequest(stream=stream))

        # Check if everything is OK
        if response.status == ResponseStatus.OK:
            print("Streaming has started")
        else:
            print("Couldn't start the stream! Reason:", response.error)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

Listening to events

Secondly, the plugin sends events from time to time. This library lets you listen to these events and handle them:

"""Example shows how to listen to events."""

import asyncio
import logging
import sys

from obswsrc import OBSWS
from obswsrc.logs import logger


# We will output logging to sys.stdout, as many events might raise errors
# on creation (that's because protocol.json is not perfect) - such errors
# are logged by obs-ws-rc automatically, we just need to see them
logger.setLevel(logging.ERROR)
logger.addHandler(logging.StreamHandler(stream=sys.stdout))


async def main():

    async with OBSWS('localhost', 4444, "password") as obsws:

        print("Connection established.")

        # We will receive events here by awaiting for them (you can await for
        # an event of a specific type by providing `type_name` argument to
        # the obsws.event() method)
        event = await obsws.event()

        # Awaited event might be None if connection is closed
        while event is not None:
            print("Awaited for '{}' event!".format(event.type_name))
            event = await obsws.event()

        print("Connection terminated.")

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

Protocol description

The protocol requrests, responses and events are declared in the PROTOCOL.md by the authors of the obs-websocket plugin.

However, there are minor mistakes in that file. And the field naming of the original protocol is inconsistent. For example, there’re fields like authRequired, at the same time there’re plenty of fields that use a hyphen as a word separator (like kbits-per-sec).

This library internally maps such fields to more pythonic names (auth_required and kbits_per_sec as such) - that allows for convenient passing fields as keyword arguments.

The version of the protocol used by this library can be found in ./obswsrc/protocol.json.

Package: obswsrc

Submodules

obswsrc.client module

exception obswsrc.client.AuthError[source]

Bases: Exception

Raised by OBSWS.connect() if authentication has failed.

class obswsrc.client.OBSWS(host, port=4444, password=None, *, skip_auth=False, loop=None)[source]

Bases: object

Main class used for obs-websocket communication. Can be used as a context manager (given you use it in async with statement).

Example usage:

async with OBSWS("localhost") as obsws:
    ...

This is an equivalent to the following:

obsws = OBSWS("localhost")
await obsws.connect()

try:
    ...
finally:
    await obsws.close()

This class also supports Future-like protocol (it implements __await__() method). You can await for the OBSWS instance for it to close:

await obsws

Note

When entering the context manager (using async with statement), you should be ready to except AuthError that might raise due to failed auth, or OSError that can be raised by the underlying websockets library in case of being unable to connect to OBS Studio.

See also

connect() close()

Parameters:
  • host (str) – Server host
  • port (int) – Server port
  • password (str|None) – Server password (if needed)
  • skip_auth (bool) – Whether or not to skip authentication
  • loop (asyncio.AbstractEventLoop|None) – Event loop to use
close()[source]

Clean shutdown. Consequent calls on an already closed instance have not effect.

Note

This method is a coroutine.

closed

Return whether or not this OBSWS instance is closed.

connect()[source]

Establish connection to the server, start the event loop and perform authentication (the latter can be skipped with skip_auth argument in __init__()).

Raises:
  • ValueError – if already connected
  • AuthError – if auth is enabled but password is invalid or not not set
  • OSError – raised by the underlying websockets library if connection attempt is failed

Note

This method is a coroutine.

event(type_name=None)[source]

Return a future that, when awaited for, returns an event of type type_name. If type_name is None, the future result will be the first occurred event. If connection is closed while future is not done, the future result is None.

Parameters:type_name (str|None) – Event type to await for, None to await for an event of any type
Returns:Future
Return type:asyncio.Future
Raises:ValueError – if not connected

Changed in version 2.3.0: This method is not a coroutine now, but it returns a asyncio.Future object.

host

The host that OBSWS was instantiated with (read-only).

Returns:Server host
Return type:str
password

The port that OBSWS was instantiated with (read-only).

Returns:Server password (None if not given)
Return type:str|None
port

The port that OBSWS was instantiated with (read-only).

Returns:Server port
Return type:int
register_event_handler(type_name, callback)[source]

Register event handler (either a regular one or an async-coroutine).

Parameters:
  • type_name – Event name
  • callback (callable) – Function or coroutine function
Raises:

ValueError – if callback is already registered for the event

Deprecated since version 2.2: Use event() instead.

require(request)[source]

Send a request to the server and await, return the response.

Parameters:request (requests.BaseRequest) – Fully formed request
Returns:Response from the server (None if the connection was closed during communication)
Return type:requests.BaseResponse|None
Raises:ValueError – if not connected

Note

This method is a coroutine.

unregister_event_handler(type_name, callback)[source]

Unregister previously registered event handler.

Parameters:
  • type_name – Event name
  • callback (callable) – Function or coroutine function

Deprecated since version 2.2: Use event() instead.

obswsrc.events module

This module holds dynamically generated classes. For more info see protocol.py and protocol.json.

class obswsrc.events.BaseEvent(*args, **kwargs)[source]

Bases: obswsrc.struct.Struct

type_name
class obswsrc.events.BaseEventMeta(name, bases, namespace)[source]

Bases: obswsrc.struct.StructMeta

obswsrc.protocol module

exception obswsrc.protocol.UnknownType[source]

Bases: Exception

obswsrc.protocol.build_events(protocol, known_types)[source]
obswsrc.protocol.build_requests(protocol, known_types)[source]
obswsrc.protocol.build_types(protocol)[source]

obswsrc.requests module

This module holds dynamically generated classes. For more info see protocol.py and protocol.json.

class obswsrc.requests.BaseRequest(*args, **kwargs)[source]

Bases: obswsrc.struct.Struct

get_request_data(message_id)[source]
class response_class(*args, **kwargs)[source]

Bases: obswsrc.requests.BaseResponse

BaseRequest.type_name
class obswsrc.requests.BaseResponse(*args, **kwargs)[source]

Bases: obswsrc.struct.Struct

class obswsrc.requests.BaseResponseMeta(name, bases, namespace)[source]

Bases: obswsrc.struct.StructMeta

class obswsrc.requests.ResponseStatus[source]

Bases: enum.Enum

An enumeration.

ERROR = 'ERROR'
OK = 'OK'
obswsrc.requests.dummy_request(**kwargs)[source]

obswsrc.struct module

class obswsrc.struct.BaseStruct[source]

Bases: dict

class obswsrc.struct.Struct(*args, **kwargs)[source]

Bases: obswsrc.struct.BaseStruct

class obswsrc.struct.StructField(attr_name, field_name, value_type, optional=False)[source]

Bases: object

class obswsrc.struct.StructMeta(name, bases, namespace)[source]

Bases: type

class obswsrc.struct.VariableStruct(**kwargs)[source]

Bases: obswsrc.struct.BaseStruct

obswsrc.types module

This module holds dynamically generated classes. For more info see protocol.py and protocol.json.

Protocol Reference

Types

bool

  • Style: This type is native to Python

float

  • Style: This type is native to Python

int

  • Style: This type is native to Python

str

  • Style: This type is native to Python

Font

  • Style: This type contains statically typed fields
  • Fields:
    • FACE
      • type: str
      • pythonic name: face
      • internal name: face
      • is optional? Yes
    • FLAGS
      • type: int
      • pythonic name: flags
      • internal name: flags
      • is optional? Yes
    • SIZE
      • type: int
      • pythonic name: size
      • internal name: size
      • is optional? Yes
    • STYLE
      • type: str
      • pythonic name: style
      • internal name: style
      • is optional? Yes

Profile

  • Style: This type contains statically typed fields
  • Fields:
    • PROFILE NAME
      • type: str
      • pythonic name: profile_name
      • internal name: profile-name
      • is optional? No

profile_list

  • Style: This type represents a list of objects of other type
  • Item type: Profile

scene_collection_list

  • Style: This type represents a list of objects of other type
  • Item type: SceneCollection

scene_list

  • Style: This type represents a list of objects of other type
  • Item type: source_list

SceneCollection

  • Style: This type contains statically typed fields
  • Fields:
    • SC NAME
      • type: str
      • pythonic name: sc_name
      • internal name: sc-name
      • is optional? No

SceneSource

  • Style: This type contains statically typed fields
  • Fields:
    • NAME
      • type: str
      • pythonic name: name
      • internal name: name
      • is optional? No
    • TYPE
      • type: str
      • pythonic name: type
      • internal name: type
      • is optional? No
    • VOLUME
      • type: float
      • pythonic name: volume
      • internal name: volume
      • is optional? No
    • X
      • type: float
      • pythonic name: x
      • internal name: x
      • is optional? No
    • Y
      • type: float
      • pythonic name: y
      • internal name: y
      • is optional? No
    • SOURCE CX
      • type: int
      • pythonic name: source_cx
      • internal name: source_cx
      • is optional? No
    • SOURCE CY
      • type: int
      • pythonic name: source_cy
      • internal name: source_cy
      • is optional? No
    • CX
      • type: float
      • pythonic name: cx
      • internal name: cx
      • is optional? No
    • CY
      • type: float
      • pythonic name: cy
      • internal name: cy
      • is optional? No
    • RENDER
      • type: bool
      • pythonic name: render
      • internal name: render
      • is optional? No

source_list

  • Style: This type represents a list of objects of other type
  • Item type: SceneSource

Stream

  • Style: This type contains statically typed fields
  • Fields:
    • SETTINGS
      • type: StreamSettings
      • pythonic name: settings
      • internal name: settings
      • is optional? Yes
    • TYPE
      • type: str
      • pythonic name: type
      • internal name: type
      • is optional? Yes
    • METADATA
      • type: StreamMetadata
      • pythonic name: metadata
      • internal name: metadata
      • is optional? Yes

StreamMetadata

  • Style: The number and types of the fields can vary
  • Allowed types: str float int bool

StreamSettings

  • Style: This type contains statically typed fields
  • Fields:
    • SERVER
      • type: str
      • pythonic name: server
      • internal name: server
      • is optional? Yes
    • KEY
      • type: str
      • pythonic name: key
      • internal name: key
      • is optional? Yes
    • USE AUTH
      • type: bool
      • pythonic name: use_auth
      • internal name: use-auth
      • is optional? Yes
    • USERNAME
      • type: str
      • pythonic name: username
      • internal name: username
      • is optional? Yes
    • PASSWORD
      • type: str
      • pythonic name: password
      • internal name: password
      • is optional? Yes

Transition

  • Style: This type contains statically typed fields
  • Fields:
    • NAME
      • type: str
      • pythonic name: name
      • internal name: name
      • is optional? Yes
    • DURATION
      • type: int
      • pythonic name: duration
      • internal name: duration
      • is optional? Yes

transition_name_list

  • Style: This type represents a list of objects of other type
  • Item type: TransitionName

TransitionName

  • Style: This type contains statically typed fields
  • Fields:
    • NAME
      • type: str
      • pythonic name: name
      • internal name: name
      • is optional? No

Requests

Authenticate

DisableStudioMode

EnableStudioMode

GetAuthRequired

  • Description: view PROTOCOL.md entry on ‘GetAuthRequired’
  • Request Fields:
  • Response Fields:
    • AUTH REQUIRED
      • type: bool
      • pythonic name: auth_required
      • internal name: authRequired
      • is optional? No
    • CHALLENGE
      • type: str
      • pythonic name: challenge
      • internal name: challenge
      • is optional? Yes
    • SALT
      • type: str
      • pythonic name: salt
      • internal name: salt
      • is optional? Yes

GetCurrentProfile

GetCurrentScene

GetCurrentSceneCollection

GetCurrentTransition

GetMute

  • Description: view PROTOCOL.md entry on ‘GetMute’
  • Request Fields:
    • SOURCE
      • type: str
      • pythonic name: source
      • internal name: source
      • is optional? No
  • Response Fields:
    • NAME
      • type: str
      • pythonic name: name
      • internal name: name
      • is optional? No
    • MUTED
      • type: bool
      • pythonic name: muted
      • internal name: muted
      • is optional? No

GetPreviewScene

GetRecordingFolder

GetSceneList

GetSpecialSources

  • Description: view PROTOCOL.md entry on ‘GetSpecialSources’
  • Request Fields:
  • Response Fields:
    • DESKTOP1
      • type: str
      • pythonic name: desktop1
      • internal name: desktop-1
      • is optional? Yes
    • DESKTOP2
      • type: str
      • pythonic name: desktop2
      • internal name: desktop-2
      • is optional? Yes
    • MIC1
      • type: str
      • pythonic name: mic1
      • internal name: mic-1
      • is optional? Yes
    • MIC2
      • type: str
      • pythonic name: mic2
      • internal name: mic-2
      • is optional? Yes
    • MIC3
      • type: str
      • pythonic name: mic3
      • internal name: mic-3
      • is optional? Yes

GetStreamingStatus

  • Description: view PROTOCOL.md entry on ‘GetStreamingStatus’
  • Request Fields:
  • Response Fields:
    • STREAMING
      • type: bool
      • pythonic name: streaming
      • internal name: streaming
      • is optional? No
    • RECORDING
      • type: bool
      • pythonic name: recording
      • internal name: recording
      • is optional? No
    • STREAM TIMECODE
      • type: str
      • pythonic name: stream_timecode
      • internal name: stream-timecode
      • is optional? Yes
    • REC TIMECODE
      • type: str
      • pythonic name: rec_timecode
      • internal name: rec-timecode
      • is optional? Yes
    • PREVIEW ONLY
      • type: bool
      • pythonic name: preview_only
      • internal name: preview-only
      • is optional? No

GetStreamSettings

GetStudioModeStatus

GetTextGDIPlusProperties

  • Description: view PROTOCOL.md entry on ‘GetTextGDIPlusProperties’
  • Request Fields:
    • SOURCE
      • type: str
      • pythonic name: source
      • internal name: source
      • is optional? No
    • SCENE NAME
      • type: str
      • pythonic name: scene_name
      • internal name: scene-name
      • is optional? Yes
  • Response Fields:
    • ALIGN
      • type: str
      • pythonic name: align
      • internal name: align
      • is optional? No
    • BK COLOR
      • type: int
      • pythonic name: bk_color
      • internal name: bk_color
      • is optional? No
    • BK OPACITY
      • type: int
      • pythonic name: bk_opacity
      • internal name: bk_opacity
      • is optional? No
    • CHATLOG
      • type: bool
      • pythonic name: chatlog
      • internal name: chatlog
      • is optional? No
    • CHATLOG LINES
      • type: int
      • pythonic name: chatlog_lines
      • internal name: chatlog_lines
      • is optional? No
    • COLOR
      • type: int
      • pythonic name: color
      • internal name: color
      • is optional? No
    • EXTENTS
      • type: bool
      • pythonic name: extents
      • internal name: extents
      • is optional? No
    • EXTENTS WRAP
      • type: bool
      • pythonic name: extents_wrap
      • internal name: extents_wrap
      • is optional? No
    • EXTENTS CX
      • type: int
      • pythonic name: extents_cx
      • internal name: extents_cx
      • is optional? No
    • EXTENTS CY
      • type: int
      • pythonic name: extents_cy
      • internal name: extents_cy
      • is optional? No
    • FILE
      • type: str
      • pythonic name: file
      • internal name: file
      • is optional? No
    • READ FROM FILE
      • type: bool
      • pythonic name: read_from_file
      • internal name: read_from_file
      • is optional? No
    • FONT
      • type: Font
      • pythonic name: font
      • internal name: font
      • is optional? No
    • GRADIENT
      • type: bool
      • pythonic name: gradient
      • internal name: gradient
      • is optional? No
    • GRADIENT COLOR
      • type: int
      • pythonic name: gradient_color
      • internal name: gradient_color
      • is optional? No
    • GRADIENT DIR
      • type: float
      • pythonic name: gradient_dir
      • internal name: gradient_dir
      • is optional? No
    • GRADIENT OPACITY
      • type: int
      • pythonic name: gradient_opacity
      • internal name: gradient_opacity
      • is optional? No
    • OUTLINE
      • type: bool
      • pythonic name: outline
      • internal name: outline
      • is optional? No
    • OUTLINE COLOR
      • type: int
      • pythonic name: outline_color
      • internal name: outline_color
      • is optional? No
    • OUTLINE SIZE
      • type: int
      • pythonic name: outline_size
      • internal name: outline_size
      • is optional? No
    • OUTLINE OPACITY
      • type: int
      • pythonic name: outline_opacity
      • internal name: outline_opacity
      • is optional? No
    • TEXT
      • type: str
      • pythonic name: text
      • internal name: text
      • is optional? No
    • VALIGN
      • type: bool
      • pythonic name: valign
      • internal name: valign
      • is optional? No
    • VERTICAL
      • type: bool
      • pythonic name: vertical
      • internal name: vertical
      • is optional? No
    • RENDER
      • type: bool
      • pythonic name: render
      • internal name: render
      • is optional? No

GetTransitionDuration

GetTransitionList

GetVersion

  • Description: view PROTOCOL.md entry on ‘GetVersion’
  • Request Fields:
  • Response Fields:
    • VERSION
      • type: float
      • pythonic name: version
      • internal name: version
      • is optional? No
    • OBS WEBSOCKET VERSION
      • type: str
      • pythonic name: obs_websocket_version
      • internal name: obs-websocket-version
      • is optional? No
    • OBS STUDIO VERSION
      • type: str
      • pythonic name: obs_studio_version
      • internal name: obs-studio-version
      • is optional? No

GetVolume

  • Description: view PROTOCOL.md entry on ‘GetVolume’
  • Request Fields:
    • SOURCE
      • type: str
      • pythonic name: source
      • internal name: source
      • is optional? No
  • Response Fields:
    • NAME
      • type: str
      • pythonic name: name
      • internal name: name
      • is optional? No
    • VOLUME
      • type: float
      • pythonic name: volume
      • internal name: volume
      • is optional? No
    • MUTED
      • type: bool
      • pythonic name: muted
      • internal name: muted
      • is optional? No

ListProfiles

ListSceneCollections

SaveStreamSettings

SetCurrentProfile

SetCurrentScene

SetCurrentSceneCollection

SetCurrentTransition

SetMute

  • Description: view PROTOCOL.md entry on ‘SetMute’
  • Request Fields:
    • SOURCE
      • type: str
      • pythonic name: source
      • internal name: source
      • is optional? No
    • MUTE
      • type: bool
      • pythonic name: mute
      • internal name: mute
      • is optional? No
  • Response Fields:

SetPreviewScene

SetRecordingFolder

SetSceneItemCrop

  • Description: view PROTOCOL.md entry on ‘SetSceneItemCrop’
  • Request Fields:
    • ITEM
      • type: str
      • pythonic name: item
      • internal name: item
      • is optional? No
    • SCENE NAME
      • type: str
      • pythonic name: scene_name
      • internal name: scene-name
      • is optional? No
    • TOP
      • type: int
      • pythonic name: top
      • internal name: top
      • is optional? No
    • BOTTOM
      • type: int
      • pythonic name: bottom
      • internal name: bottom
      • is optional? No
    • LEFT
      • type: int
      • pythonic name: left
      • internal name: left
      • is optional? No
    • RIGHT
      • type: int
      • pythonic name: right
      • internal name: right
      • is optional? No
  • Response Fields:

SetSceneItemPosition

  • Description: view PROTOCOL.md entry on ‘SetSceneItemPosition’
  • Request Fields:
    • ITEM
      • type: str
      • pythonic name: item
      • internal name: item
      • is optional? No
    • X
      • type: float
      • pythonic name: x
      • internal name: x
      • is optional? No
    • Y
      • type: float
      • pythonic name: y
      • internal name: y
      • is optional? No
    • SCENE NAME
      • type: str
      • pythonic name: scene_name
      • internal name: scene-name
      • is optional? No
  • Response Fields:

SetSceneItemTransform

  • Description: view PROTOCOL.md entry on ‘SetSceneItemTransform’
  • Request Fields:
    • ITEM
      • type: str
      • pythonic name: item
      • internal name: item
      • is optional? No
    • X SCALE
      • type: float
      • pythonic name: x_scale
      • internal name: x-scale
      • is optional? No
    • Y SCALE
      • type: float
      • pythonic name: y_scale
      • internal name: y-scale
      • is optional? No
    • ROTATION
      • type: float
      • pythonic name: rotation
      • internal name: rotation
      • is optional? No
    • SCENE NAME
      • type: str
      • pythonic name: scene_name
      • internal name: scene-name
      • is optional? No
  • Response Fields:

SetSourceRender

  • Description: view PROTOCOL.md entry on ‘SetSourceRender’
  • Request Fields:
    • SOURCE
      • type: str
      • pythonic name: source
      • internal name: source
      • is optional? No
    • RENDER
      • type: bool
      • pythonic name: render
      • internal name: render
      • is optional? No
    • SCENE NAME
      • type: str
      • pythonic name: scene_name
      • internal name: scene-name
      • is optional? Yes
  • Response Fields:

SetStreamSettings

  • Description: view PROTOCOL.md entry on ‘SetStreamSettings’
  • Request Fields:
    • TYPE
      • type: str
      • pythonic name: type
      • internal name: type
      • is optional? No
    • SETTINGS
      • type: StreamSettings
      • pythonic name: settings
      • internal name: settings
      • is optional? No
    • SAVE
      • type: bool
      • pythonic name: save
      • internal name: save
      • is optional? No
  • Response Fields:
    • TYPE
      • type: str
      • pythonic name: type
      • internal name: type
      • is optional? No
    • SETTINGS
      • type: StreamSettings
      • pythonic name: settings
      • internal name: settings
      • is optional? No

SetTransitionDuration

SetVolume

  • Description: view PROTOCOL.md entry on ‘SetVolume’
  • Request Fields:
    • SOURCE
      • type: str
      • pythonic name: source
      • internal name: source
      • is optional? No
    • VOLUME
      • type: float
      • pythonic name: volume
      • internal name: volume
      • is optional? No
  • Response Fields:

StartRecording

StartStopRecording

StartStopStreaming

StartStreaming

StopRecording

StopStreaming

ToggleMute

ToggleStudioMode

TransitionToProgram

Events

Exiting

PreviewSceneChanged

ProfileChanged

ProfileListChanged

RecordingStarted

RecordingStarting

RecordingStopped

RecordingStopping

SceneCollectionChanged

SceneCollectionListChanged

SceneItemAdded

  • Description: view PROTOCOL.md entry on ‘SceneItemAdded’
  • Request Fields:
    • SCENE NAME
      • type: str
      • pythonic name: scene_name
      • internal name: scene-name
      • is optional? No
    • ITEM NAME
      • type: str
      • pythonic name: item_name
      • internal name: item-name
      • is optional? No
  • Response Fields:

SceneItemRemoved

  • Description: view PROTOCOL.md entry on ‘SceneItemRemoved’
  • Request Fields:
    • SCENE NAME
      • type: str
      • pythonic name: scene_name
      • internal name: scene-name
      • is optional? No
    • ITEM NAME
      • type: str
      • pythonic name: item_name
      • internal name: item-name
      • is optional? No
  • Response Fields:

SceneItemVisibilityChanged

  • Description: view PROTOCOL.md entry on ‘SceneItemVisibilityChanged’
  • Request Fields:
    • SCENE NAME
      • type: str
      • pythonic name: scene_name
      • internal name: scene-name
      • is optional? No
    • ITEM NAME
      • type: str
      • pythonic name: item_name
      • internal name: item-name
      • is optional? No
    • ITEM VISIBLE
      • type: bool
      • pythonic name: item_visible
      • internal name: item-visible
      • is optional? No
  • Response Fields:

ScenesChanged

SourceOrderChanged

StreamStarted

StreamStarting

StreamStatus

  • Description: view PROTOCOL.md entry on ‘StreamStatus’
  • Request Fields:
    • STREAMING
      • type: bool
      • pythonic name: streaming
      • internal name: streaming
      • is optional? No
    • RECORDING
      • type: bool
      • pythonic name: recording
      • internal name: recording
      • is optional? No
    • PREVIEW ONLY
      • type: bool
      • pythonic name: preview_only
      • internal name: preview-only
      • is optional? No
    • BYTES PER SEC
      • type: int
      • pythonic name: bytes_per_sec
      • internal name: bytes-per-sec
      • is optional? No
    • KBITS PER SEC
      • type: int
      • pythonic name: kbits_per_sec
      • internal name: kbits-per-sec
      • is optional? No
    • STRAIN
      • type: float
      • pythonic name: strain
      • internal name: strain
      • is optional? No
    • TOTAL STREAM TIME
      • type: int
      • pythonic name: total_stream_time
      • internal name: total-stream-time
      • is optional? No
    • NUM TOTAL FRAMES
      • type: int
      • pythonic name: num_total_frames
      • internal name: num-total-frames
      • is optional? No
    • NUM DROPPED FRAMES
      • type: int
      • pythonic name: num_dropped_frames
      • internal name: num-dropped-frames
      • is optional? No
    • FPS
      • type: float
      • pythonic name: fps
      • internal name: fps
      • is optional? No
  • Response Fields:

StreamStopped

StreamStopping

StudioModeSwitched

SwitchScenes

SwitchTransition

TransitionBegin

TransitionDurationChanged

TransitionListChanged

Introduction

What’s this?

obs-ws-rc is a Python 3.5+ library that allows you to establish client connections to the obs-websocket plugin for OBS Studio.

It’s based on asyncio-approach which it inherited from the underlying WebSocket library - websockets

Performing requests

Firstly, obs-websocket‘s protocol provides you with the ability to send requests and retrieve responses to and from OBS Studio.

Let’s see how it’s done with obs-ws-rc:

"""Example shows how to send requests and get responses."""

import asyncio

from obswsrc import OBSWS
from obswsrc.requests import ResponseStatus, StartStreamingRequest
from obswsrc.types import Stream, StreamSettings


async def main():

    async with OBSWS('localhost', 4444, "password") as obsws:

        # We can send an empty StartStreaming request (in that case the plugin
        # will use OBS configuration), but let's provide some settings as well
        stream_settings = StreamSettings(
            server="rtmp://example.org/my_application",
            key="secret_stream_key",
            use_auth=False
        )
        stream = Stream(
            settings=stream_settings,
            type="rtmp_custom",
        )

        # Now let's actually perform a request
        response = await obsws.require(StartStreamingRequest(stream=stream))

        # Check if everything is OK
        if response.status == ResponseStatus.OK:
            print("Streaming has started")
        else:
            print("Couldn't start the stream! Reason:", response.error)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

Listening to events

Secondly, the plugin sends events from time to time. This library lets you listen to these events and handle them:

"""Example shows how to listen to events."""

import asyncio
import logging
import sys

from obswsrc import OBSWS
from obswsrc.logs import logger


# We will output logging to sys.stdout, as many events might raise errors
# on creation (that's because protocol.json is not perfect) - such errors
# are logged by obs-ws-rc automatically, we just need to see them
logger.setLevel(logging.ERROR)
logger.addHandler(logging.StreamHandler(stream=sys.stdout))


async def main():

    async with OBSWS('localhost', 4444, "password") as obsws:

        print("Connection established.")

        # We will receive events here by awaiting for them (you can await for
        # an event of a specific type by providing `type_name` argument to
        # the obsws.event() method)
        event = await obsws.event()

        # Awaited event might be None if connection is closed
        while event is not None:
            print("Awaited for '{}' event!".format(event.type_name))
            event = await obsws.event()

        print("Connection terminated.")

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

Protocol description

The protocol requrests, responses and events are declared in the PROTOCOL.md by the authors of the obs-websocket plugin.

However, there are minor mistakes in that file. And the field naming of the original protocol is inconsistent. For example, there’re fields like authRequired, at the same time there’re plenty of fields that use a hyphen as a word separator (like kbits-per-sec).

This library internally maps such fields to more pythonic names (auth_required and kbits_per_sec as such) - that allows for convenient passing fields as keyword arguments.

The version of the protocol used by this library can be found in ./obswsrc/protocol.json.

Indices and tables