Python Script to connect to OBS Websocket Server Help

mud_punk

New Member
Goal of the script: Send command to OBS to make it do something, like switch scenes, change Sources, play a gif, etc.

I've tried using Postman to get an example, and while I can connect, I get errors about the websocket version from the client being out of date when I try and SEND something to the server.

The below script is not run inside of OBS, but it is run on the same machine. I believe it does connect, but the SEND seems to have no effect. The reason for not wanting to use something like obsws_python library is due to some of the limitations (while you can switch scene's you can't switch source as it's not implemented if I read their docs correctly)

I know there are existing bots that do what I want, but I'm trying to learn and would like to do this with my own custom built bot if possible. Any help is appreciated as I've become quite frustrated with my own inability to make this work.

Python:
import websocket
import json

def test():
    ws = websocket.create_connection("ws://localhost:4444")
    scene_data = {
        "request-type": "SetCurrentScene",
        "scene-name": "TestScene" 
    }
    print(json.dumps(scene_data))
    ws.send(json.dumps(scene_data))
    ws.close()
  
test()
 

mud_punk

New Member
You know when someone figures something out and then they say "Yeah, I did it" and then that's it. No solution. God I hate that.

Some of the following may be obvious, some not, I'm just posting it all. It's probably more representitive of the journey I had trying to figure this out. Yes I know I could use other existing libraries, but I'm an idiot who likes to know how to use the core thing. Judge all you won't, I won't be responding to this thread ever again. probably.

Anyway, hope this helps someone.

Learning Curve:

  • Docs for the stuff are good, but some things tripped me up.
    • https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md
    • when it says "requestId" it apparently means whatever the hell you want. Call it "myProjectIsPissingMeOffAHHHHH" for all anyone cares
    • json format is...well represented, so this is on me, but I didn't realize that I needed everything in the request parameters regardless of it being "optional" or not. So, example:
      • requestData:
        • JSON:
          {"op": 6, "d": {"requestId": "GetMeSomeFriggenScenesYo", "requestType": "GetSceneList", "requestData": {}}}
        • "requestData" is optional...in so much that not all of them have something to pass here, but you still need to include it even if the thing you are requesting has no other data options. To show the difference, the below changes the Scene, take note of requestData
        • JSON:
          {"op": 6, "d": {"requestId": "SetMeSomeFriggenScenesYo", "requestType": "SetCurrentProgramScene", "requestData": {"sceneName":"WhateverTheHellYouNamedYourScene"}}}
    • I'm salty of SALT
      • I don't know jack about authentication other than I type in a password and stuff happens.
      • While the docs say the SALT and CHALLENGE are returned from the websocket server, I couldn't figure out for the life of me how to get them...until I realized they are just given at the point you connect (before authentication). Ain't doin' not knowin'.
So, taking this into account, looking at my first post, the correct response would be: Read the docs more.

Python:
import base64
import hashlib
import json
logging.basicConfig(level=logging.DEBUG)
import websocket

LOG = logging.getLogger(__name__)

host = "localhost"
port = 4455 #or whatever port you use
password = "YourWebsocketPassword"

id = 1

ws = websocket.WebSocket()
url = "ws://{}:{}".format(host, port)
ws.connect(url)

def _build_auth_string(salt, challenge):
    secret = base64.b64encode(
        hashlib.sha256(
            (password + salt).encode('utf-8')
        ).digest()
    )
    auth = base64.b64encode(
        hashlib.sha256(
            secret + challenge.encode('utf-8')
        ).digest()
    ).decode('utf-8')
    return auth



def _auth():
    message = ws.recv()
    result = json.loads(message) 
    server_version = result['d'].get('obsWebSocketVersion')
    auth = _build_auth_string(result['d']['authentication']['salt'], result['d']['authentication']['challenge'])

    payload = {
        "op": 1,
        "d": {
            "rpcVersion": 1,
            "authentication": auth,
            "eventSubscriptions": 1000 
        }
    }
    ws.send(json.dumps(payload))
    message = ws.recv()
    # Message Identified...or so we assume...probably good to check if this is empty or not.
    result = json.loads(message)

_auth()


# lets switch to a scene
payload =  {"op": 6, "d": {"requestId": "SetMeSomeFrigginScenesYo", "requestType": "SetCurrentProgramScene", "requestData": {"sceneName":"SuperCoolScene"}}}
ws.send(json.dumps(payload))
message=ws.recv()
print(message)

# get me my damn scenes already
payload =  {"op": 6, "d": {"requestId": "GetMeSomeFrigginScenesYo", "requestType": "GetSceneList", "requestData": {}}}
ws.send(json.dumps(payload))
message=ws.recv()    
print(message)

# shit worked, now lets change the scene cuz why not.
payload =  {"op": 6, "d": {"requestId": "SetMeSomeFrigginScenesYo", "requestType": "SetCurrentProgramScene", "requestData": {"sceneName":"AnotherSuperCoolScene"}}} #you could parse the GetSceneList and switch to one of those
ws.send(json.dumps(payload))
message=ws.recv()    
print(message)

ws.close()
 

DogRocketeer

New Member
I made an account just to tell you that youre the fukn man.

I disagree with your assessment about "just read the docs more". I read the docs..... I got as far as getting my scenes to switch by hammering it with the same command over and over til it stuck. At least a simple sample script in their docs would improve it. I was able to automate the shit out of everything thanks to your post.

Good job.
 

gomesar9

New Member
I made an account just to tell you that youre the fukn man.(2)

I disagree with your assessment about "just read the docs more". I read the docs..... I got as far as getting my scenes to switch by hammering it with the same command over and over til it stuck. At least a simple sample script in their docs would improve it. I was able to automate the shit out of everything thanks to your post.

Thank you for share information, I was stuck using "obs-websocket-py".. couldn't change scenes and don't know why.. but now I will use raw communication (that I personally prefer too), with your code I can easily understand how the api works, and your explanation about the "optional" will save me much time.

Good job. (2)
 

DogRocketeer

New Member
as an update, if you're willing to put the time in to get used to the UI, streamer.bot makes all of this a lot easier and manageable, hooks into StreamDeck as well.
 
Top