Skip to content
This repository was archived by the owner on Jan 14, 2022. It is now read-only.

Begin/create sources #11

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
default:
go build -gcflags="-e" .

8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![GoDoc](https://godoc.org/github.com/christopher-dG/go-obs-websocket?status.svg)](https://godoc.org/github.com/christopher-dG/go-obs-websocket)

`obsws` provides client functionality for [`obs-websocket`](https://github.com/Palakis/obs-websocket).
Currently, the target version is `4.4`.
Currently, the target version is `4.8`.

## Installation

Expand All @@ -25,8 +25,12 @@ import (
)

func main() {
// Optionally create a logger

logger := log.New(os.Stdout, "obsws ", log.Lstdflags)

// Connect a client.
c := obsws.Client{Host: "localhost", Port: 4444}
c := obsws.Client{Host: "localhost", Port: 4444, logger: Logger}
if err := c.Connect(); err != nil {
log.Fatal(err)
}
Expand Down
6 changes: 5 additions & 1 deletion client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package obsws

import (
"log"
"strconv"
"sync"
"time"
Expand All @@ -24,6 +25,7 @@ type Client struct {
Port int // Port (OBS default is 4444).
Password string // Password (OBS default is "").
conn *websocket.Conn // Underlying connection to OBS.
Logger *log.Logger // Logger to use for most messages
receiveTimeout time.Duration // Maximum blocking time for receiving request responses
connected bool // True until Disconnect is called.
handlers map[string]func(e Event) // Event handlers.
Expand All @@ -33,7 +35,7 @@ type Client struct {
// poll listens for responses/events.
// This function blocks until Disconnect is called.
func (c *Client) poll() {
Logger.Println("started polling")
c.Logger.Println("started polling")

for c.connected {
m := make(map[string]interface{})
Expand Down Expand Up @@ -77,6 +79,8 @@ func GetMessageID() string {

// mapToStruct serializes a map into a struct.
func mapToStruct(data map[string]interface{}, dest interface{}) error {
// func mapToStruct(resp Response, dest interface{}) error {
// data := resp.(map[string])
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
TagName: "json",
Result: dest,
Expand Down
11 changes: 8 additions & 3 deletions client_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ func (c *Client) Connect() error {
c.handlers = make(map[string]func(Event))
c.respQ = make(chan map[string]interface{}, bufferSize)

// Setup default logger
if c.Logger == nil {
c.Logger = Logger
}

conn, err := connectWS(c.Host, c.Port)
if err != nil {
return err
Expand All @@ -33,14 +38,14 @@ func (c *Client) Connect() error {
}

if !respGAR.AuthRequired {
Logger.Println("logged in (no authentication required)")
c.Logger.Println("logged in (no authentication required)")
c.connected = true
go c.poll()
return nil
}

auth := getAuth(c.Password, respGAR.Salt, respGAR.Challenge)
Logger.Println("auth:", auth)
c.Logger.Println("auth:", auth)

reqA := NewAuthenticateRequest(auth)
if err = c.conn.WriteJSON(reqA); err != nil {
Expand All @@ -55,7 +60,7 @@ func (c *Client) Connect() error {
return errors.New(respA.Error())
}

Logger.Println("logged in (authentication successful)")
c.Logger.Println("logged in (authentication successful)")
c.connected = true
go c.poll()
return nil
Expand Down
4 changes: 2 additions & 2 deletions client_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (c *Client) handleEvent(m map[string]interface{}) {

eventFn, ok := eventMap[t]
if !ok {
Logger.Println("unknown event type:", m["update-type"])
c.Logger.Println("unknown event type:", m["update-type"])
return
}
event := eventFn()
Expand All @@ -46,7 +46,7 @@ func (c *Client) handleEvent(m map[string]interface{}) {
}

if err := mapToStruct(m, event); err != nil {
Logger.Println("event handler failed:", err)
c.Logger.Println("event handler failed:", err)
return
}

Expand Down
4 changes: 2 additions & 2 deletions client_requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func (c *Client) SendRequest(req Request) (chan map[string]interface{}, error) {
if err := c.conn.WriteJSON(req); err != nil {
return nil, err
}
Logger.Println("sent request", req.ID())
c.Logger.Println("sent request", req.ID())
go func() { future <- c.receive(req.ID()) }()
return future, nil
}
Expand All @@ -33,7 +33,7 @@ func (c *Client) receive(id string) map[string]interface{} {
for {
resp := <-c.respQ
if resp["message-id"] == id {
Logger.Println("received response", resp["message-id"])
c.Logger.Println("received response", resp["message-id"])
return resp
}
c.respQ <- resp
Expand Down
8 changes: 6 additions & 2 deletions codegen/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@
"string": "string",
"array": "[]interface{}",
"array<string>": "[]string",
"string | object": "interface{}",
"object": "map[string]interface{}",
"output": "map[string]interface{}",
"array<object>": "[]map[string]interface{}",
"array<output>": "[]map[string]interface{}",
"array<scene>": "[]*Scene",
"array<sceneitem>": "[]*SceneItem",
"array<scenescollection>": "[]*ScenesCollection",
"array<sceneitemtransform>": "[]*SceneItemTransform",
"obsstats": "*OBSStats",
"sceneitemtransform": "*SceneItemTransform",
}
Expand Down Expand Up @@ -157,7 +160,7 @@ def gen_request(data: Dict) -> str:
}}

// Receive waits for the response.
func (r {data["name"]}Request) Receive() ({data["name"]}Response, error) {{
func (r {data["name"]}Request) Receive() (Response, error) {{
if !r.sent {{
return {data["name"]}Response{{}}, ErrNotSent
}}
Expand All @@ -181,7 +184,7 @@ def gen_request(data: Dict) -> str:
}}

// SendReceive sends the request then immediately waits for the response.
func (r {data["name"]}Request) SendReceive(c Client) ({data["name"]}Response, error) {{
func (r {data["name"]}Request) SendReceive(c Client) (Response, error) {{
if err := r.Send(c); err != nil {{
return {data["name"]}Response{{}}, err
}}
Expand Down Expand Up @@ -385,6 +388,7 @@ def newlinify(s: str, comment: bool = True) -> str:

def optional_type(s: str) -> Tuple[str, bool]:
"""Determine if a type is optional and parse the actual type name."""
s = s.lower()
if s.endswith("(optional)"):
return s[: s.find("(optional)")].strip(), True
return s, False
Expand Down
39 changes: 39 additions & 0 deletions event_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ var eventMap = map[string]func() Event{
"TransitionListChanged": func() Event { return &TransitionListChangedEvent{} },
"TransitionDurationChanged": func() Event { return &TransitionDurationChangedEvent{} },
"TransitionBegin": func() Event { return &TransitionBeginEvent{} },
"TransitionEnd": func() Event { return &TransitionEndEvent{} },
"TransitionVideoEnd": func() Event { return &TransitionVideoEndEvent{} },
"ProfileChanged": func() Event { return &ProfileChangedEvent{} },
"ProfileListChanged": func() Event { return &ProfileListChangedEvent{} },
"StreamStarting": func() Event { return &StreamStartingEvent{} },
Expand All @@ -36,17 +38,28 @@ var eventMap = map[string]func() Event{
"SourceDestroyed": func() Event { return &SourceDestroyedEvent{} },
"SourceVolumeChanged": func() Event { return &SourceVolumeChangedEvent{} },
"SourceMuteStateChanged": func() Event { return &SourceMuteStateChangedEvent{} },
"SourceAudioDeactivated": func() Event { return &SourceAudioDeactivatedEvent{} },
"SourceAudioActivated": func() Event { return &SourceAudioActivatedEvent{} },
"SourceAudioSyncOffsetChanged": func() Event { return &SourceAudioSyncOffsetChangedEvent{} },
"SourceAudioMixersChanged": func() Event { return &SourceAudioMixersChangedEvent{} },
"SourceRenamed": func() Event { return &SourceRenamedEvent{} },
"SourceFilterAdded": func() Event { return &SourceFilterAddedEvent{} },
"SourceFilterRemoved": func() Event { return &SourceFilterRemovedEvent{} },
"SourceFilterVisibilityChanged": func() Event { return &SourceFilterVisibilityChangedEvent{} },
"SourceFiltersReordered": func() Event { return &SourceFiltersReorderedEvent{} },
"MediaPlaying": func() Event { return &MediaPlayingEvent{} },
"MediaPaused": func() Event { return &MediaPausedEvent{} },
"MediaRestarted": func() Event { return &MediaRestartedEvent{} },
"MediaStopped": func() Event { return &MediaStoppedEvent{} },
"MediaNext": func() Event { return &MediaNextEvent{} },
"MediaPrevious": func() Event { return &MediaPreviousEvent{} },
"MediaStarted": func() Event { return &MediaStartedEvent{} },
"MediaEnded": func() Event { return &MediaEndedEvent{} },
"SourceOrderChanged": func() Event { return &SourceOrderChangedEvent{} },
"SceneItemAdded": func() Event { return &SceneItemAddedEvent{} },
"SceneItemRemoved": func() Event { return &SceneItemRemovedEvent{} },
"SceneItemVisibilityChanged": func() Event { return &SceneItemVisibilityChangedEvent{} },
"SceneItemLockChanged": func() Event { return &SceneItemLockChangedEvent{} },
"SceneItemTransformChanged": func() Event { return &SceneItemTransformChangedEvent{} },
"SceneItemSelected": func() Event { return &SceneItemSelectedEvent{} },
"SceneItemDeselected": func() Event { return &SceneItemDeselectedEvent{} },
Expand Down Expand Up @@ -74,6 +87,10 @@ func derefEvent(e Event) Event {
return *e
case *TransitionBeginEvent:
return *e
case *TransitionEndEvent:
return *e
case *TransitionVideoEndEvent:
return *e
case *ProfileChangedEvent:
return *e
case *ProfileListChangedEvent:
Expand Down Expand Up @@ -122,6 +139,10 @@ func derefEvent(e Event) Event {
return *e
case *SourceMuteStateChangedEvent:
return *e
case *SourceAudioDeactivatedEvent:
return *e
case *SourceAudioActivatedEvent:
return *e
case *SourceAudioSyncOffsetChangedEvent:
return *e
case *SourceAudioMixersChangedEvent:
Expand All @@ -136,6 +157,22 @@ func derefEvent(e Event) Event {
return *e
case *SourceFiltersReorderedEvent:
return *e
case *MediaPlayingEvent:
return *e
case *MediaPausedEvent:
return *e
case *MediaRestartedEvent:
return *e
case *MediaStoppedEvent:
return *e
case *MediaNextEvent:
return *e
case *MediaPreviousEvent:
return *e
case *MediaStartedEvent:
return *e
case *MediaEndedEvent:
return *e
case *SourceOrderChangedEvent:
return *e
case *SceneItemAddedEvent:
Expand All @@ -144,6 +181,8 @@ func derefEvent(e Event) Event {
return *e
case *SceneItemVisibilityChangedEvent:
return *e
case *SceneItemLockChangedEvent:
return *e
case *SceneItemTransformChangedEvent:
return *e
case *SceneItemSelectedEvent:
Expand Down
4 changes: 3 additions & 1 deletion events_general.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package obsws

// HeartbeatEvent : Emitted every 2 seconds after enabling it by calling SetHeartbeat.
//
// Since obs-websocket version: V0.3.
//
// https://github.com/Palakis/obs-websocket/blob/4.x-current/docs/generated/protocol.md#heartbeat
type HeartbeatEvent struct {
// Toggles between every JSON message as an "I am alive" indicator.
Expand Down Expand Up @@ -46,7 +48,7 @@ type HeartbeatEvent struct {
_event `json:",squash"`
}

// BroadcastCustomMessageEvent : A custom broadcast message was received.
// BroadcastCustomMessageEvent : A custom broadcast message, sent by the server, requested by one of the websocket clients.
//
// Since obs-websocket version: 4.7.0.
//
Expand Down
Loading