A complete Go implementation of the OBS WebSocket 5.x protocol client for connecting to and controlling OBS Studio.
- Full OBS WebSocket 5.x protocol support
- Authentication with SHA256 challenge/response
- Event subscription system with bitmask filtering
- Request/response handling with timeouts
- Batch request support for multiple operations
- Automatic connection management
- Type-safe message handling
- Concurrent event processing
package main
import (
"log"
"time"
"github.com/Tilican/obs-websocket-go"
)
func main() {
config := obswebsocket.Config{
URL: "ws://localhost:4455",
Password: "your-password-here", // Leave empty if no auth
EventSubscriptions: obswebsocket.EventAll,
Timeout: 30 * time.Second,
}
client := obswebsocket.NewClient(config)
// Add event handler
client.AddEventHandler(func(event *obswebsocket.EventData) {
log.Printf("Event: %s", event.EventType)
})
// Connect
if err := client.Connect(); err != nil {
log.Fatal(err)
}
defer client.Disconnect()
// Get version
version, err := client.GetVersion()
if err != nil {
log.Printf("Error: %v", err)
} else {
log.Printf("OBS Version: %v", version)
}
}Control which events you receive using bitmask flags:
config := obswebsocket.Config{
URL: "ws://localhost:4455",
EventSubscriptions: obswebsocket.EventScenes |
obswebsocket.EventOutputs |
obswebsocket.EventInputs,
}You can update your event subscriptions after connecting using Reidentify:
client := obswebsocket.NewClient(config)
client.Connect()
// Initially subscribed to scenes, outputs, inputs
// Now subscribe to different events
newSubscriptions := obswebsocket.EventGeneral |
obswebsocket.EventTransitions |
obswebsocket.EventFilters
err := client.Reidentify(newSubscriptions)
if err != nil {
log.Printf("Failed to update subscriptions: %v", err)
}
// Or subscribe to all events except high-volume ones
err = client.Reidentify(obswebsocket.EventAll)Available event categories:
EventGeneral- General eventsEventConfig- Configuration changesEventScenes- Scene operationsEventInputs- Input/source eventsEventTransitions- Transition eventsEventFilters- Filter eventsEventOutputs- Stream/record eventsEventSceneItems- Scene item eventsEventMediaInputs- Media input eventsEventVendors- Vendor eventsEventUi- UI eventsEventAll- All standard events (excludes high-volume)
High-volume events (must be explicitly subscribed):
EventInputVolumeMetersEventInputActiveStateChangedEventInputShowStateChangedEventSceneItemTransformChanged
// Get scene list
scenes, err := client.GetSceneList()
// Set current scene
err := client.SetCurrentProgramScene("Scene Name")
// Create scene
response, err := client.SendRequest("CreateScene", map[string]interface{}{
"sceneName": "New Scene",
})// Get stream status
status, err := client.GetStreamStatus()
// Start streaming
err := client.StartStream()
// Stop streaming
err := client.StopStream()// Get OBS stats
stats, err := client.GetStats()Execute multiple requests atomically:
// Using builder pattern
builder := obswebsocket.NewBatchRequestBuilder().
SetHaltOnFailure(true).
AddRequest("GetVersion", nil).
AddRequest("GetSceneList", nil).
AddRequest("SetCurrentProgramScene", map[string]interface{}{
"sceneName": "Scene 1",
})
response, err := client.SendBatchBuilder(builder)
// Or using predefined operations
operations := []obswebsocket.SceneOperation{
{Type: "SetCurrentProgramScene", SceneName: "Scene 1"},
{Type: "CreateScene", SceneName: "New Scene"},
}
response, err := client.BatchSceneOperations(operations)Send any OBS WebSocket request:
response, err := client.SendRequest("GetInputSettings", map[string]interface{}{
"inputName": "Camera",
})
if response.RequestStatus.Result {
var settings map[string]interface{}
json.Unmarshal(response.ResponseData, &settings)
// Use settings...
}The client handles OBS WebSocket authentication automatically using the SHA256 challenge/response system:
- Server sends Hello with authentication challenge
- Client generates response:
SHA256(SHA256(password + salt) + challenge) - Server validates and confirms identification
The client provides detailed error information:
response, err := client.SendRequest("SetCurrentProgramScene", map[string]interface{}{
"sceneName": "NonExistent",
})
if err != nil {
log.Printf("Request error: %v", err)
} else if !response.RequestStatus.Result {
log.Printf("OBS error %d: %s",
response.RequestStatus.Code,
response.RequestStatus.Comment)
}This implementation supports OBS WebSocket protocol version 5.x with:
- All OpCodes (Hello, Identify, Event, Request, etc.)
- Complete message type definitions
- All request status codes
- Event subscription bitmasks
- Batch request execution types
- Authentication challenge/response
The client is designed for concurrent use:
- Connection state is protected with RWMutex
- Event handlers are called in separate goroutines
- Request/response correlation is thread-safe
- Multiple requests can be sent simultaneously
github.com/gorilla/websocket- WebSocket implementationgithub.com/google/uuid- UUID generation for request IDs- Standard library packages for crypto, JSON, etc.