mirror of
				https://github.com/onyx-and-iris/OBS-to-XAir.git
				synced 2025-11-03 23:11:51 +00:00 
			
		
		
		
	update for websocket v5, obs 28
support reading conn info from toml add setup.py for easy dep installation
This commit is contained in:
		
							parent
							
								
									f96a80e6da
								
							
						
					
					
						commit
						ba3389882e
					
				
							
								
								
									
										3
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								LICENSE
									
									
									
									
									
								
							@ -1,6 +1,7 @@
 | 
				
			|||||||
MIT License
 | 
					MIT License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Copyright (c) 2019-2021 lebaston100
 | 
					Copyright (c) 2019-2022 lebaston100
 | 
				
			||||||
 | 
					Copyright (c) 2022 Onyx-and-Iris
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
of this software and associated documentation files (the "Software"), to deal
 | 
					of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										66
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								README.md
									
									
									
									
									
								
							@ -1,50 +1,58 @@
 | 
				
			|||||||
# OBS-to-XAir
 | 
					# OBS-to-XAir
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This is a small script that mutes and unmutes channels on Behringer XAir Mixers depending on the current scene.
 | 
					This is a small script that mutes and unmutes channels on Behringer XAir Mixers depending on the current scene.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Requirements
 | 
					## Requirements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- The [obs-websocket plugin](https://github.com/Palakis/obs-websocket/releases) (Version 4.5.1 - 4.9.1)
 | 
					-   The [obs-websocket-v5 plugin](https://github.com/obsproject/obs-websocket/releases) (Version 5.0.0+)
 | 
				
			||||||
- A fairly recent version of Python 3
 | 
					    -   Note, this now comes included with OBS 28+
 | 
				
			||||||
- [websocket-client](https://github.com/websocket-client/websocket-client)
 | 
					-   [obsws-python](https://github.com/aatikturk/obsws-python)
 | 
				
			||||||
- [python-osc](https://github.com/attwad/python-osc)
 | 
					-   [xair-api](https://github.com/onyx-and-iris/xair-api-python)
 | 
				
			||||||
 | 
					-   Python 3.10 or greater
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## General Setup
 | 
					## Installation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Install Python 3.x.x 
 | 
					-   First install [latest version of Python](https://www.python.org/downloads/).
 | 
				
			||||||
  - On Windows: Make sure you trick "Add Python 3.x to PATH" in the setup
 | 
					
 | 
				
			||||||
- Make sure you also install pip
 | 
					    -   Ensure you tick `Add Python 3.x to PATH` during setup.
 | 
				
			||||||
- Open up a command line and execute these commands to install the required pip modules:
 | 
					
 | 
				
			||||||
  - pip install python-osc
 | 
					-   Download the repository files with git or the green `Code` button. Then in command prompt:
 | 
				
			||||||
  - pip install websocket-client
 | 
					
 | 
				
			||||||
- OBS Websocket:
 | 
					```
 | 
				
			||||||
  - Download the installer from the link above and run it
 | 
					cd OBS-to-XAir
 | 
				
			||||||
  - Start OBS, open the "Tools" menu and select "websocket server settings"
 | 
					pip install .
 | 
				
			||||||
  - Make sure that "Enable Websocket server" is checked, "Server Port" is 4444 and "Enable authentification" is unchecked
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Configuration
 | 
					## Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You have to configure your scene-to-channel mapping and the IP settings. Open up the .py file with a text editor.
 | 
					-   Configure websocket settings within `OBS->Tools->obs-websocket Settings`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- The mapping:
 | 
					-   Open the included `config.toml` and set OBS host, port and password as well as the xair mixers ip.
 | 
				
			||||||
This python dict resolves the OBS scene names to the XAir channels. 3 Channels come pre-set as a template.
 | 
					 | 
				
			||||||
The format follows the rule "scene name": "mixer channel".
 | 
					 | 
				
			||||||
"scene name" is the scene name in OBS which you want to pair to a mixer channel. "mixer channel" is the channel on the XAir mixer. Important: If you use the lower channels 1-9 you have to add zero-padding so "1" becomes "01" and so on. You can find all available channel names besides the normal 01-16 in the "parameters.txt" when you download the latest mixer firmware zip.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
- OBS IP and Port:
 | 
					    -   You may also set the kind of mixer in the script. (`XR12, XR16, XR18, MR18`)
 | 
				
			||||||
In line 9 and 10 you can set the IP and port from the machine that runs OBS. If you are running the script locally you don't need to change anything.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
- XAir Mixer IP:
 | 
					-   Set the scene to channel mutes mapping in the script.
 | 
				
			||||||
In line 11 you need to set the IP address of your XAir Mixer. The OSC Port can't be changed so it's hardcoded.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Usage
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Just run the script, either from the command line or with a double-click. There will be no further output besides "Websocket open" when it's running. If the connection to OBS is broken, you will get an error.
 | 
					Simply run the script, there will be confirmation of mixer connection and OBS connection if everything is successful. Switch between the defined scenes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Closing OBS will stop the script.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Further notes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Since this script relies upon two interfaces, `obsws-python` and `xair-api` this code can be readily modified to interact with any OBS events and set any xair parameters. Check the README files for each interface for further details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Compatibility
 | 
					## Compatibility
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This script was developed and tested with:
 | 
					This script was developed and tested with:
 | 
				
			||||||
- OBS 23.1.0
 | 
					 | 
				
			||||||
- obs-websocket 4.5.1
 | 
					 | 
				
			||||||
- A Behringer XR18
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
It should theoretically also work with the XR12, XR16 and X32 but i cannot validate this myself. Feel free to let me know if it worked for you.
 | 
					-   OBS 28.01
 | 
				
			||||||
 | 
					-   obs-websocket 5.0.1
 | 
				
			||||||
 | 
					-   A Behringer XR18 and Midas MR18
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Special Thanks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-   OBS team and the obs-websocket developers + Behringer/Midas for the OSC protocol.
 | 
				
			||||||
 | 
					-   [Adem](https://github.com/aatikturk) for contributions towards the obsws-python wrapper.
 | 
				
			||||||
 | 
					-   [Onyx-and-Iris](https://github.com/onyx-and-iris) for contributions towards the obsws-python and xair-api wrappers.
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										53
									
								
								__main__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								__main__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import obsws_python as obs
 | 
				
			||||||
 | 
					import xair_api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mapping = {
 | 
				
			||||||
 | 
					    "START": 1,
 | 
				
			||||||
 | 
					    "BRB": 2,
 | 
				
			||||||
 | 
					    "END": 3,
 | 
				
			||||||
 | 
					    "LIVE": 4,
 | 
				
			||||||
 | 
					}  # set the mapping for the scene to channel mapping here. "scenename": "channel"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Observer:
 | 
				
			||||||
 | 
					    def __init__(self, mixer):
 | 
				
			||||||
 | 
					        self._mixer = mixer
 | 
				
			||||||
 | 
					        self._request = obs.ReqClient()
 | 
				
			||||||
 | 
					        self._event = obs.EventClient()
 | 
				
			||||||
 | 
					        self._event.callback.register(
 | 
				
			||||||
 | 
					            (self.on_current_program_scene_changed, self.on_exit_started)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.running = True
 | 
				
			||||||
 | 
					        resp = self._request.get_version()
 | 
				
			||||||
 | 
					        info = (
 | 
				
			||||||
 | 
					            f"Connected to OBS version:{resp.obs_version}",
 | 
				
			||||||
 | 
					            f"with websocket version:{resp.obs_web_socket_version}",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        print(" ".join(info))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def on_current_program_scene_changed(self, data):
 | 
				
			||||||
 | 
					        scene = data.scene_name
 | 
				
			||||||
 | 
					        print(f"Switched to scene {scene}")
 | 
				
			||||||
 | 
					        for k, v in mapping.items():
 | 
				
			||||||
 | 
					            self._mixer.strip[v - 1].mix.on = k == scene
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def on_exit_started(self, _):
 | 
				
			||||||
 | 
					        print("OBS closing")
 | 
				
			||||||
 | 
					        self._event.unsubscribe()
 | 
				
			||||||
 | 
					        self.running = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    with xair_api.connect(kind_mixer) as mixer:
 | 
				
			||||||
 | 
					        o = Observer(mixer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while o.running:
 | 
				
			||||||
 | 
					            time.sleep(0.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    kind_mixer = "XR18"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
							
								
								
									
										8
									
								
								config.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								config.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					[connection]
 | 
				
			||||||
 | 
					# OBS connection info
 | 
				
			||||||
 | 
					host = "localhost"
 | 
				
			||||||
 | 
					port = 4455
 | 
				
			||||||
 | 
					password = "strongpassword"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# xair mixer ip
 | 
				
			||||||
 | 
					ip = "mixer.local"
 | 
				
			||||||
@ -1,50 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import websocket, json
 | 
					 | 
				
			||||||
from time import sleep
 | 
					 | 
				
			||||||
from pythonosc import osc_message_builder
 | 
					 | 
				
			||||||
from pythonosc import udp_client
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mapping = {"scene1": "01", "scene2": "02", "scene3": "03"} #set the mapping for the scene to channel mapping here. "scenename": "channel"
 | 
					 | 
				
			||||||
obsip = "localhost"     #set the obs machine ip here. localhost works if you run this script on the same machine.
 | 
					 | 
				
			||||||
obsport = "4444"        #set the ob websocket port here. 4444 is defult
 | 
					 | 
				
			||||||
xairip = "192.168.1.10" #set the xairip here
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def ws1_on_message(ws, message):
 | 
					 | 
				
			||||||
    jsn = json.loads(message)
 | 
					 | 
				
			||||||
    if jsn["update-type"] == "SwitchScenes":
 | 
					 | 
				
			||||||
        for scene in mapping:
 | 
					 | 
				
			||||||
            if scene == jsn["scene-name"]:
 | 
					 | 
				
			||||||
                client.send_message("/ch/" + mapping[scene] + "/mix/on", 1)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                client.send_message("/ch/" + mapping[scene] + "/mix/on", 0)
 | 
					 | 
				
			||||||
    elif jsn["update-type"] == "TransitionBegin":
 | 
					 | 
				
			||||||
        for scene in mapping:
 | 
					 | 
				
			||||||
            if scene == jsn["to-scene"]:
 | 
					 | 
				
			||||||
                client.send_message("/ch/" + mapping[scene] + "/mix/on", 1)
 | 
					 | 
				
			||||||
            elif scene == jsn["from-scene"] and not jsn["name"] == "Cut":
 | 
					 | 
				
			||||||
                client.send_message("/ch/" + mapping[scene] + "/mix/on", 1)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                client.send_message("/ch/" + mapping[scene] + "/mix/on", 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def ws1_on_error(ws, error):
 | 
					 | 
				
			||||||
    print(error)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def ws1_on_close(ws):
 | 
					 | 
				
			||||||
    print("Websocket close")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def ws1_on_open(ws):
 | 
					 | 
				
			||||||
    print("Websocket open")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def ws1_start():
 | 
					 | 
				
			||||||
    while True:
 | 
					 | 
				
			||||||
        ws1.run_forever()
 | 
					 | 
				
			||||||
        print("Websocket restart")
 | 
					 | 
				
			||||||
        print("This most likely means that OBS is not open or you lost network connection.")
 | 
					 | 
				
			||||||
        sleep(1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == "__main__":
 | 
					 | 
				
			||||||
    client = udp_client.SimpleUDPClient(xairip, 10024)
 | 
					 | 
				
			||||||
    ws1 = websocket.WebSocketApp("ws://" + obsip + ":" + obsport, on_message = ws1_on_message, on_error = ws1_on_error, on_close = ws1_on_close)
 | 
					 | 
				
			||||||
    ws1.on_open = ws1_on_open
 | 
					 | 
				
			||||||
    ws1_start()
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user