From eca3834fd7ee2cf22d94b2b48c064904f982d831 Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 14 Jan 2020 23:32:53 -0500 Subject: [PATCH] initial version --- .drone.yml | 39 + go.mod | 11 + go.sum | 11 + obsws/.gitignore | 3 + obsws/.travis.yml | 6 + obsws/LICENSE | 19 + obsws/README.md | 69 + obsws/client.go | 92 + obsws/client_connection.go | 93 + obsws/client_events.go | 49 + obsws/client_requests.go | 47 + obsws/client_test.go | 11 + obsws/codegen/protocol.py | 402 ++++ obsws/doc.go | 2 + obsws/event_utils.go | 109 + obsws/events.go | 24 + obsws/events_general.go | 44 + obsws/events_other.go | 13 + obsws/events_profiles.go | 22 + obsws/events_recording.go | 40 + obsws/events_replay_buffer.go | 40 + obsws/events_scenes.go | 47 + obsws/events_sources.go | 64 + obsws/events_streaming.go | 88 + obsws/events_studio_mode.go | 32 + obsws/events_transitions.go | 59 + obsws/go.sum | 4 + obsws/logger.go | 8 + obsws/requests.go | 65 + obsws/requests_general.go | 568 +++++ obsws/requests_profiles.go | 280 +++ obsws/requests_recording.go | 453 ++++ obsws/requests_replay_buffer.go | 365 ++++ obsws/requests_scene_collections.go | 282 +++ obsws/requests_scene_items.go | 878 ++++++++ obsws/requests_scenes.go | 286 +++ obsws/requests_sources.go | 2784 +++++++++++++++++++++++++ obsws/requests_streaming.go | 757 +++++++ obsws/requests_studio_mode.go | 650 ++++++ obsws/requests_transitions.go | 470 +++++ plugin/images/actions/replay_off.png | Bin 0 -> 5145 bytes plugin/images/actions/replay_on.png | Bin 0 -> 5044 bytes plugin/images/actions/replay_save.png | Bin 0 -> 7494 bytes plugin/images/background.png | Bin 0 -> 2475 bytes plugin/images/pluginIcon.png | Bin 0 -> 6044 bytes plugin/manifest.json | 56 + plugin/pi/css/check.png | Bin 0 -> 234 bytes plugin/pi/css/check.svg | 3 + plugin/pi/css/sdpi.css | 1483 +++++++++++++ plugin/pi/index_pi.html | 44 + plugin/pi/index_pi.js | 324 +++ replay.go | 256 +++ replay_test.go | 21 + 53 files changed, 11473 insertions(+) create mode 100644 .drone.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 obsws/.gitignore create mode 100644 obsws/.travis.yml create mode 100644 obsws/LICENSE create mode 100644 obsws/README.md create mode 100644 obsws/client.go create mode 100644 obsws/client_connection.go create mode 100644 obsws/client_events.go create mode 100644 obsws/client_requests.go create mode 100644 obsws/client_test.go create mode 100644 obsws/codegen/protocol.py create mode 100644 obsws/doc.go create mode 100644 obsws/event_utils.go create mode 100644 obsws/events.go create mode 100644 obsws/events_general.go create mode 100644 obsws/events_other.go create mode 100644 obsws/events_profiles.go create mode 100644 obsws/events_recording.go create mode 100644 obsws/events_replay_buffer.go create mode 100644 obsws/events_scenes.go create mode 100644 obsws/events_sources.go create mode 100644 obsws/events_streaming.go create mode 100644 obsws/events_studio_mode.go create mode 100644 obsws/events_transitions.go create mode 100644 obsws/go.sum create mode 100644 obsws/logger.go create mode 100644 obsws/requests.go create mode 100644 obsws/requests_general.go create mode 100644 obsws/requests_profiles.go create mode 100644 obsws/requests_recording.go create mode 100644 obsws/requests_replay_buffer.go create mode 100644 obsws/requests_scene_collections.go create mode 100644 obsws/requests_scene_items.go create mode 100644 obsws/requests_scenes.go create mode 100644 obsws/requests_sources.go create mode 100644 obsws/requests_streaming.go create mode 100644 obsws/requests_studio_mode.go create mode 100644 obsws/requests_transitions.go create mode 100644 plugin/images/actions/replay_off.png create mode 100644 plugin/images/actions/replay_on.png create mode 100644 plugin/images/actions/replay_save.png create mode 100644 plugin/images/background.png create mode 100644 plugin/images/pluginIcon.png create mode 100644 plugin/manifest.json create mode 100644 plugin/pi/css/check.png create mode 100644 plugin/pi/css/check.svg create mode 100644 plugin/pi/css/sdpi.css create mode 100644 plugin/pi/index_pi.html create mode 100644 plugin/pi/index_pi.js create mode 100644 replay.go create mode 100644 replay_test.go diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..196ef53 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,39 @@ +kind: pipeline +name: default + +steps: + - name: binary + volumes: + - name: cache + path: /tmp/build + image: golang + commands: + - GOOS=windows GOARCH=amd64 go build -o /tmp/build/remote.exe + - GOOS=darwin GOARCH=amd64 go build -o /tmp/build/remote + - name: package + volumes: + - name: cache + path: /tmp/build + image: alpine:latest + commands: + - apk add --update zip jq + - cp /tmp/build/replay.exe /tmp/build/replay plugin/ + - VERSION=`cat plugin/manifest.json | jq -r '.Version'` + - echo "v$VERSION" > /tmp/build/version.txt + - mv plugin tf.meow.obsreplay.sdPlugin + - zip -r /tmp/build/tf.meow.obsreplay.streamDeckPlugin tf.meow.obsreplay.sdPlugin + - name: release + volumes: + - name: cache + path: /tmp/build + image: tystuyfzand/drone-gitea-release + settings: + tag_file: /tmp/build/version.txt + title_file: /tmp/build/version.txt + files: [ '/tmp/build/tf.meow.obsreplay.streamDeckPlugin' ] + environment: + PLUGIN_API_KEY: + from_secret: gitea_token +volumes: + - name: cache + temp: {} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b7b1ee1 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module meow.tf/streamdeck/obs-replay + +go 1.13 + +require ( + github.com/christopher-dG/go-obs-websocket v0.0.0-20181224025342-2efc3605bff5 + github.com/gorilla/websocket v1.4.1 + github.com/mitchellh/mapstructure v1.1.2 + github.com/valyala/fastjson v1.4.1 + meow.tf/streamdeck/sdk v0.0.0-20190519021527-54a933f8777d +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..92f5122 --- /dev/null +++ b/go.sum @@ -0,0 +1,11 @@ +github.com/christopher-dG/go-obs-websocket v0.0.0-20181224025342-2efc3605bff5 h1:VtKPsvxzKt/+EnkhcPp0Xg7MDjt/a+CNRSj5phITbjo= +github.com/christopher-dG/go-obs-websocket v0.0.0-20181224025342-2efc3605bff5/go.mod h1:hFg9UFHefvNCvpWpYtOaP/VT2HyokIJsmV1AUBjpTeQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= +github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= +meow.tf/streamdeck/sdk v0.0.0-20190519021527-54a933f8777d h1:PPZHRoZFy9p4GjXssLvTneJfX6cS0bEm51md5TqXFgU= +meow.tf/streamdeck/sdk v0.0.0-20190519021527-54a933f8777d/go.mod h1:SnrBz5Bcdgk/wwIvgjo+3gXiBYV6b/dYSpb2AFxjHcA= diff --git a/obsws/.gitignore b/obsws/.gitignore new file mode 100644 index 0000000..ccc9f83 --- /dev/null +++ b/obsws/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +comments.json +protocol.md diff --git a/obsws/.travis.yml b/obsws/.travis.yml new file mode 100644 index 0000000..0186322 --- /dev/null +++ b/obsws/.travis.yml @@ -0,0 +1,6 @@ +language: go +go: + - 1.10.x + - master +notifications: + email: false diff --git a/obsws/LICENSE b/obsws/LICENSE new file mode 100644 index 0000000..9daf867 --- /dev/null +++ b/obsws/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 Chris de Graaf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/obsws/README.md b/obsws/README.md new file mode 100644 index 0000000..6970926 --- /dev/null +++ b/obsws/README.md @@ -0,0 +1,69 @@ +# obsws + +[![Build Status](https://travis-ci.com/christopher-dG/go-obs-websocket.svg?branch=master)](https://travis-ci.com/christopher-dG/go-obs-websocket) +[![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`. + +## Installation + +```sh +go get github.com/christopher-dG/go-obs-websocket +``` + +## Usage + +```go +package main + +import ( + "log" + "time" + + "github.com/christopher-dG/go-obs-websocket" +) + +func main() { + // Connect a client. + c := obsws.Client{Host: "localhost", Port: 4444} + if err := c.Connect(); err != nil { + log.Fatal(err) + } + defer c.Disconnect() + + // Send and receive a request asynchronously. + req := obsws.NewGetStreamingStatusRequest() + if err := req.Send(c); err != nil { + log.Fatal(err) + } + // This will block until the response comes (potentially forever). + resp, err := req.Receive() + if err != nil { + log.Fatal(err) + } + log.Println("streaming:", resp.Streaming) + + // Set the amount of time we can wait for a response. + obsws.SetReceiveTimeout(time.Second * 2) + + // Send and receive a request synchronously. + req = obsws.NewGetStreamingStatusRequest() + // Note that we create a new request, + // because requests have IDs that must be unique. + // This will block for up to two seconds, since we set a timeout. + resp, err = req.SendReceive(c) + if err != nil { + log.Fatal(err) + } + log.Println("streaming:", resp.Streaming) + + // Respond to events by registering handlers. + c.AddEventHandler("SwitchScenes", func(e obsws.Event) { + // Make sure to assert the actual event type. + log.Println("new scene:", e.(obsws.SwitchScenesEvent).SceneName) + }) + + time.Sleep(time.Second * 10) +} +``` diff --git a/obsws/client.go b/obsws/client.go new file mode 100644 index 0000000..538b260 --- /dev/null +++ b/obsws/client.go @@ -0,0 +1,92 @@ +package obsws + +import ( + "strconv" + "sync" + "time" + + "github.com/gorilla/websocket" + "github.com/mitchellh/mapstructure" +) + +const bufferSize = 100 + +var ( + receiveTimeout = time.Duration(0) + messageID = 0 + lock = sync.Mutex{} +) + +// Client is the interface to obs-websocket. +// Client{Host: "localhost", Port: 4444} will probably work if you haven't configured OBS. +type Client struct { + Host string // Host (probably "localhost"). + Port int // Port (OBS default is 4444). + Password string // Password (OBS default is ""). + conn *websocket.Conn // Underlying connection to OBS. + 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. + respQ chan map[string]interface{} // Queue of received responses. +} + +// poll listens for responses/events. +// This function blocks until Disconnect is called. +func (c *Client) poll() { + Logger.Println("started polling") + + for c.connected { + m := make(map[string]interface{}) + if err := c.conn.ReadJSON(&m); err != nil { + if !c.connected { + return + } + Logger.Println("read from WS:", err) + continue + } + + if _, ok := m["message-id"]; ok { + c.handleResponse(m) + } else { + c.handleEvent(m) + } + } +} + +// Connected returns wheter or not the client is connected. +func (c *Client) Connected() bool { + return c.connected +} + +// SetReceiveTimeout sets the maximum blocking time for receiving request responses. +// If set to 0 (the default), there is no timeout. +func SetReceiveTimeout(timeout time.Duration) { + receiveTimeout = timeout +} + +// getMessageID generates a string that the client has not yet used. +func getMessageID() string { + lock.Lock() + messageID++ + id := strconv.Itoa(messageID) + lock.Unlock() + return id +} + +// mapToStruct serializes a map into a struct. +func mapToStruct(data map[string]interface{}, dest interface{}) error { + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + TagName: "json", + Result: dest, + }) + if err != nil { + Logger.Println("initializing decoder:", err) + return err + } + if err = decoder.Decode(data); err != nil { + Logger.Printf("unmarshalling map -> %T: %v", dest, err) + Logger.Printf("input: %#v\n", data) + return err + } + return nil +} diff --git a/obsws/client_connection.go b/obsws/client_connection.go new file mode 100644 index 0000000..1228d68 --- /dev/null +++ b/obsws/client_connection.go @@ -0,0 +1,93 @@ +package obsws + +import ( + "crypto/sha256" + "encoding/base64" + "errors" + "fmt" + + "github.com/gorilla/websocket" +) + +// Connect opens a WebSocket connection and authenticates if necessary. +func (c *Client) Connect() error { + c.handlers = make(map[string]func(Event)) + c.respQ = make(chan map[string]interface{}, bufferSize) + + conn, err := connectWS(c.Host, c.Port) + if err != nil { + return err + } + c.conn = conn + + // We can't use SendReceive yet because we haven't started polling. + + reqGAR := NewGetAuthRequiredRequest() + if err = c.conn.WriteJSON(reqGAR); err != nil { + return err + } + + respGAR := &GetAuthRequiredResponse{} + if err = c.conn.ReadJSON(respGAR); err != nil { + return err + } + + if !respGAR.AuthRequired { + 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) + + reqA := NewAuthenticateRequest(auth) + if err = c.conn.WriteJSON(reqA); err != nil { + return err + } + + respA := &AuthenticateResponse{} + if err = c.conn.ReadJSON(respA); err != nil { + return err + } + if respA.Status() != "ok" { + return errors.New(respA.Error()) + } + + Logger.Println("logged in (authentication successful)") + c.connected = true + go c.poll() + return nil +} + +// Disconnect closes the WebSocket connection. +func (c *Client) Disconnect() error { + c.connected = false + if err := c.conn.Close(); err != nil { + return err + } + return nil +} + +// connectWS opens the WebSocket connection. +func connectWS(host string, port int) (*websocket.Conn, error) { + url := fmt.Sprintf("ws://%s:%d", host, port) + Logger.Println("connecting to", url) + conn, _, err := websocket.DefaultDialer.Dial(url, nil) + if err != nil { + return nil, err + } + return conn, nil +} + +// getAuth computes the auth challenge response. +func getAuth(password, salt, challenge string) string { + sha := sha256.Sum256([]byte(password + salt)) + b64 := base64.StdEncoding.EncodeToString([]byte(sha[:])) + + sha = sha256.Sum256([]byte(b64 + challenge)) + b64 = base64.StdEncoding.EncodeToString([]byte(sha[:])) + + return b64 +} diff --git a/obsws/client_events.go b/obsws/client_events.go new file mode 100644 index 0000000..c4c8212 --- /dev/null +++ b/obsws/client_events.go @@ -0,0 +1,49 @@ +package obsws + +import ( + "errors" + "log" +) + +// ErrUnknownEventType is returned when a handler is added for an unknown event. +var ErrUnknownEventType = errors.New("unknown event type") + +// AddEventHandler adds a handler function for a given event type. +func (c *Client) AddEventHandler(eventType string, handler func(Event)) error { + if eventMap[eventType] == nil { + return ErrUnknownEventType + } + c.handlers[eventType] = handler + return nil +} + +// RemoveEventHandler removes the handler for a given event type. +func (c *Client) RemoveEventHandler(eventType string) { + delete(c.handlers, eventType) +} + +// handleEvent runs an event's handler if it exists. +func (c *Client) handleEvent(m map[string]interface{}) { + t := m["update-type"].(string) + + log.Println("Handle", t) + + eventFn, ok := eventMap[t] + if !ok { + Logger.Println("unknown event type:", m["update-type"]) + return + } + event := eventFn() + + handler, ok := c.handlers[t] + if !ok { + return + } + + if err := mapToStruct(m, event); err != nil { + Logger.Println("event handler failed:", err) + return + } + + go handler(derefEvent(event)) +} diff --git a/obsws/client_requests.go b/obsws/client_requests.go new file mode 100644 index 0000000..360bb3c --- /dev/null +++ b/obsws/client_requests.go @@ -0,0 +1,47 @@ +package obsws + +import ( + "errors" + "time" +) + +const interval = time.Millisecond * 50 + +var ( + // ErrNotConnected is returned when a request is sent by a client which is not connected. + ErrNotConnected = errors.New("not connected") + // ErrReceiveTimeout is returned when a response takes too long to arrive. + ErrReceiveTimeout = errors.New("receive timed out") +) + +// sendRequest sends a request to the WebSocket server. +func (c *Client) sendRequest(req Request) (chan map[string]interface{}, error) { + if !c.connected { + return nil, ErrNotConnected + } + future := make(chan map[string]interface{}) + if err := c.conn.WriteJSON(req); err != nil { + return nil, err + } + Logger.Println("sent request", req.ID()) + go func() { future <- c.receive(req.ID()) }() + return future, nil +} + +// receive waits until a response matching the given ID arrives. +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"]) + return resp + } + c.respQ <- resp + time.Sleep(interval) + } +} + +// handleResponse sends a response into the queue. +func (c *Client) handleResponse(m map[string]interface{}) { + c.respQ <- m +} diff --git a/obsws/client_test.go b/obsws/client_test.go new file mode 100644 index 0000000..7890ce8 --- /dev/null +++ b/obsws/client_test.go @@ -0,0 +1,11 @@ +package obsws + +import "testing" + +func TestGetAuth(t *testing.T) { + expected := "zTM5ki6L2vVvBQiTG9ckH1Lh64AbnCf6XZ226UmnkIA=" + observed := getAuth("password", "salt", "challenge") + if observed != expected { + t.Errorf("expected auth == '%s', got '%s'", expected, observed) + } +} diff --git a/obsws/codegen/protocol.py b/obsws/codegen/protocol.py new file mode 100644 index 0000000..9542b1d --- /dev/null +++ b/obsws/codegen/protocol.py @@ -0,0 +1,402 @@ +# TODO: No warning is given for struct fields whose names match function names. +# Maybe bring the reserved list back. + +import json +import os +import sys +from typing import Dict, List, Tuple + +package = "obsws" + +doc = "https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md" + +disclaimer = """ +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py +""" + +# TODO: Test the less clear ones. +type_map = { + "bool": "bool", + "boolean": "bool", + "int": "int", + "integer": "int", + "float": "float64", + "double": "float64", + "string": "string", + "array": "[]interface{}", + "object": "map[string]interface{}", + "array of objects": "[]map[string]interface{}", + "object|array": "interface{}", + "scene|array": "[]map[string]interface{}", + "source|array": "[]map[string]interface{}", +} + +unknown_types = [] + + +def process_json(d: Dict): + """Generate Go code for the entire protocol file.""" + if "--events" in sys.argv or "--all" in sys.argv: + gen_events(d["events"]) + if "--requests" in sys.argv or "--all" in sys.argv: + gen_requests(d["requests"]) + + +def gen_category(prefix: str, category: str, data: Dict): + """Generate all events or requests in one category.""" + func = gen_event if prefix == "events" else gen_request + content = "\n".join( + filter( + lambda s: not s.isspace(), + "\n".join(func(thing) for thing in data).split("\n"), + ) + ) + + with open(f"{prefix}_{category}.go".replace(" ", "_"), "w") as f: + f.write( + f""" + package {package} + + {disclaimer} + + {content} + """ + ) + + +def gen_events(events: Dict): + """Generate all events.""" + for category, data in events.items(): + gen_category("events", category, data) + gen_event_utils(events) + + +def gen_event(data: Dict) -> str: + """Write Go code with a type definition and interface functions.""" + struct = f""" + type {data["name"]}Event struct {{ + {go_struct_variables(go_variables(data.get("returns", [])))} + _event `json:",squash"` + }} + """ + description = newlinify(f"{data['name']}Event : {data['description']}") + if not description.endswith("."): + description += "." + if data.get("since"): + description += ( + f"\n//\n// Since obs-websocket version: {data['since'].capitalize()}." + ) + + return f""" + {description} + // + // {doc}#{data["heading"]["text"].lower()} + {struct} + """ + + +def gen_requests(requests: Dict): + """Generate all requests and responses.""" + for category, data in requests.items(): + gen_category("requests", category, data) + + +def gen_request(data: Dict) -> str: + """Write Go code with type definitions and interface functions.""" + struct = f""" + type {data["name"]}Request struct {{ + {go_struct_variables(go_variables(data.get("params", [])))} + _request `json:",squash"` + response chan {data["name"]}Response + }} + """ + + description = newlinify(f"{data['name']}Request : {data['description']}") + if description and not description.endswith("."): + description += "." + if data.get("since"): + description += ( + f"\n//\n// Since obs-websocket version: {data['since'].capitalize()}." + ) + + request = f""" + {description} + // + // {doc}#{data["heading"]["text"].lower()} + {struct} + + {gen_request_new(data)} + + // Send sends the request. + func (r *{data["name"]}Request) Send(c *Client) error {{ + if r.sent {{ + return ErrAlreadySent + }} + future, err := c.sendRequest(r) + if err != nil {{ + return err + }} + r.sent = true + go func() {{ + m := <-future + var resp {data["name"]}Response + if err = mapToStruct(m, &resp); err != nil {{ + r.err <- err + }} else if resp.Status() != StatusOK {{ + r.err <- errors.New(resp.Error()) + }} else {{ + r.response <- resp + }} + }}() + return nil + }} + + // Receive waits for the response. + func (r {data["name"]}Request) Receive() ({data["name"]}Response, error) {{ + if !r.sent {{ + return {data["name"]}Response{{}}, ErrNotSent + }} + if receiveTimeout == 0 {{ + select {{ + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return {data["name"]}Response{{}}, err + }} + }} else {{ + select {{ + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return {data["name"]}Response{{}}, err + case <-time.After(receiveTimeout): + return {data["name"]}Response{{}}, ErrReceiveTimeout + }} + }} + }} + + // SendReceive sends the request then immediately waits for the response. + func (r {data["name"]}Request) SendReceive(c *Client) ({data["name"]}Response, error) {{ + if err := r.Send(c); err != nil {{ + return {data["name"]}Response{{}}, err + }} + return r.Receive() + }} + """ + + if data.get("returns"): + struct = f""" + type {data["name"]}Response struct {{ + {go_struct_variables(go_variables(data["returns"]))} + _response `json:",squash"` + }} + """ + else: + struct = ( + f"""type {data["name"]}Response struct {{ _response `json:",squash"`}}""" + ) + + description = f"// {data['name']}Response : Response for {data['name']}Request." + if data.get("since"): + description += ( + f"\n//\n// Since obs-websocket version: {data['since'].capitalize()}." + ) + + response = f""" + {description} + // + // {doc}#{data["heading"]["text"].lower()} + {struct} + """ + + return f"{request}\n\n{response}" + + +def gen_request_new(request: Dict): + """Generate Go code with a New___Request function for a request type.""" + base = f""" + // New{request["name"]}Request returns a new {request["name"]}Request. + func New{request["name"]}Request(\ + """ + variables = go_variables(request.get("params", []), export=False) + default_args = f""" + _request{{ + ID_: getMessageID(), + Type_: "{request["name"]}", + err: make(chan error, 1), + }}, + make(chan {request["name"]}Response, 1), + """ + if not variables: + sig = f"{base}) {request['name']}Request" + constructor_args = f"""{{ + {default_args} + }} + """ + else: + args = "\n".join( + f"{'_type' if var['name'] == 'type' else var['name']} {var['type']}," + for var in variables + ) + constructor_args = ( + "{\n" + + "\n".join( + "_type," if var["name"] == "type" else f"{var['name']}," + for var in variables + ) + + default_args + + "}" + ) + if len(variables) == 1: + sig = f"{base}{args}) {request['name']}Request" + else: + sig = f""" + {base} + {args} + ) {request["name"]}Request\ + """ + return f"{sig} {{ return {request['name']}Request{constructor_args} }}" + + +def gen_event_utils(events: Dict): + """ + Generate a Go file with a mappings from type names to structs, + and a function for dereferencing interface pointers. + """ + event_map = {} + event_list = [] + for category in events.values(): + for e in category: + event_map[e["name"]] = f"func() Event {{ return &{e['name']}Event{{}} }}" + event_list.append(f"*{e['name']}Event") + event_entries = "\n".join(f'"{k}": {v},' for k, v in event_map.items()) + event_cases = "\n".join(f"case {e}:\nreturn *e" for e in event_list) + + deref = f""" + func derefEvent(e Event) Event {{ + switch e := e.(type) {{ + {event_cases} + default: + return nil + }} + }} + """ + + with open("event_utils.go", "w") as f: + f.write( + f""" + package {package} + + {disclaimer} + + var eventMap = map[string]func() Event {{ + {event_entries} + }} + + // derefEvent returns an Event struct from a pointer to an Event struct. + {deref} + """ + ) + + +def go_variables(variables: List[Dict], export: bool = True) -> str: + """ + Convert a list of variable names into Go code to be put + inside a struct definition. + """ + vardicts, varnames = [], [] + for v in variables: + typename, optional = optional_type(v["type"]) + varname = go_var(v["name"], export=export) + vardicts.append( + { + "name": varname, + "type": type_map[typename.lower()], + "tag": f'`json:"{v["name"]}"`', + "description": v["description"].replace("\n", " "), + "optional": optional, + "unknown": typename.lower() in unknown_types, + "actual_type": v["type"], + "duplicate": varname in varnames, + } + ) + varnames.append(varname) + return vardicts + + +def go_var(s: str, export: bool = True) -> str: + """Convert a variable name in the input file to a Go variable name.""" + s = f"{(str.upper if export else str.lower)(s[0])}{s[1:]}" + for sep in ["-", "_", ".*.", "[].", "."]: + while sep in s: + _len = len(sep) + if s.endswith(sep): + s = s[:-_len] + continue + i = s.find(sep) + s = f"{s[:i]}{s[i+_len].upper()}{s[i+_len+1:]}" + + return s.replace("Id", "ID").replace("Obs", "OBS").replace("Fps", "FPS") + + +def go_struct_variables(variables: List[Dict]) -> str: + """Generate Go code containing struct field definitions.""" + lines = [] + for var in variables: + if var["description"]: + description = ( + var["description"] + .replace("e.g. ", "e.g.") + .replace(". ", "\n") + .replace("e.g.", "e.g. ") + ) + for desc_line in description.split("\n"): + desc_line = desc_line.strip() + if desc_line and not desc_line.endswith("."): + desc_line += "." + lines.append(f"// {desc_line}") + lines.append(f"// Required: {'Yes' if not var['optional'] else 'No'}.") + todos = [] + if var["unknown"]: + todos.append(f"Unknown type ({var['actual_type']})") + if var["duplicate"]: + todos.append("Duplicate name") + todos = " ".join(f"TODO: {todo}." for todo in todos) + if todos: + lines.append(f"// {todos}") + lines.append(f"{var['name']} {var['type']} {var['tag']}") + return "\n".join(lines) + + +def newlinify(s: str, comment: bool = True) -> str: + """Put each sentence of a string onto its own line.""" + s = s.replace("e.g. ", "e.g.").replace(". ", "\n").replace("e.g.", "e.g. ") + if comment: + s = "\n".join( + [f"// {_s}" if not _s.startswith("//") else _s for _s in s.split("\n")] + ) + return s + + +def optional_type(s: str) -> Tuple[str, bool]: + """Determine if a type is optional and parse the actual type name.""" + if s.endswith("(optional)"): + return s[: s.find("(optional)")].strip(), True + return s, False + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Missing filename argument") + exit(1) + + if not os.path.isfile(sys.argv[1]): + print(f"file '{sys.argv[1]}' does not exist") + exit(1) + + with open(sys.argv[1]) as f: + d = json.load(f) + + process_json(d) + os.system("goimports -w *.go") diff --git a/obsws/doc.go b/obsws/doc.go new file mode 100644 index 0000000..03396da --- /dev/null +++ b/obsws/doc.go @@ -0,0 +1,2 @@ +// Package obsws provides client functionality for obs-websocket. +package obsws diff --git a/obsws/event_utils.go b/obsws/event_utils.go new file mode 100644 index 0000000..a40226b --- /dev/null +++ b/obsws/event_utils.go @@ -0,0 +1,109 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +var eventMap = map[string]func() Event{ + "SwitchScenes": func() Event { return &SwitchScenesEvent{} }, + "ScenesChanged": func() Event { return &ScenesChangedEvent{} }, + "SceneCollectionChanged": func() Event { return &SceneCollectionChangedEvent{} }, + "SceneCollectionListChanged": func() Event { return &SceneCollectionListChangedEvent{} }, + "SwitchTransition": func() Event { return &SwitchTransitionEvent{} }, + "TransitionListChanged": func() Event { return &TransitionListChangedEvent{} }, + "TransitionDurationChanged": func() Event { return &TransitionDurationChangedEvent{} }, + "TransitionBegin": func() Event { return &TransitionBeginEvent{} }, + "ProfileChanged": func() Event { return &ProfileChangedEvent{} }, + "ProfileListChanged": func() Event { return &ProfileListChangedEvent{} }, + "StreamStarting": func() Event { return &StreamStartingEvent{} }, + "StreamStarted": func() Event { return &StreamStartedEvent{} }, + "StreamStopping": func() Event { return &StreamStoppingEvent{} }, + "StreamStopped": func() Event { return &StreamStoppedEvent{} }, + "StreamStatus": func() Event { return &StreamStatusEvent{} }, + "RecordingStarting": func() Event { return &RecordingStartingEvent{} }, + "RecordingStarted": func() Event { return &RecordingStartedEvent{} }, + "RecordingStopping": func() Event { return &RecordingStoppingEvent{} }, + "RecordingStopped": func() Event { return &RecordingStoppedEvent{} }, + "ReplayStarting": func() Event { return &ReplayStartingEvent{} }, + "ReplayStarted": func() Event { return &ReplayStartedEvent{} }, + "ReplayStopping": func() Event { return &ReplayStoppingEvent{} }, + "ReplayStopped": func() Event { return &ReplayStoppedEvent{} }, + "Exiting": func() Event { return &ExitingEvent{} }, + "Heartbeat": func() Event { return &HeartbeatEvent{} }, + "SourceOrderChanged": func() Event { return &SourceOrderChangedEvent{} }, + "SceneItemAdded": func() Event { return &SceneItemAddedEvent{} }, + "SceneItemRemoved": func() Event { return &SceneItemRemovedEvent{} }, + "SceneItemVisibilityChanged": func() Event { return &SceneItemVisibilityChangedEvent{} }, + "PreviewSceneChanged": func() Event { return &PreviewSceneChangedEvent{} }, + "StudioModeSwitched": func() Event { return &StudioModeSwitchedEvent{} }, +} + +// derefEvent returns an Event struct from a pointer to an Event struct. + +func derefEvent(e Event) Event { + switch e := e.(type) { + case *SwitchScenesEvent: + return *e + case *ScenesChangedEvent: + return *e + case *SceneCollectionChangedEvent: + return *e + case *SceneCollectionListChangedEvent: + return *e + case *SwitchTransitionEvent: + return *e + case *TransitionListChangedEvent: + return *e + case *TransitionDurationChangedEvent: + return *e + case *TransitionBeginEvent: + return *e + case *ProfileChangedEvent: + return *e + case *ProfileListChangedEvent: + return *e + case *StreamStartingEvent: + return *e + case *StreamStartedEvent: + return *e + case *StreamStoppingEvent: + return *e + case *StreamStoppedEvent: + return *e + case *StreamStatusEvent: + return *e + case *RecordingStartingEvent: + return *e + case *RecordingStartedEvent: + return *e + case *RecordingStoppingEvent: + return *e + case *RecordingStoppedEvent: + return *e + case *ReplayStartingEvent: + return *e + case *ReplayStartedEvent: + return *e + case *ReplayStoppingEvent: + return *e + case *ReplayStoppedEvent: + return *e + case *ExitingEvent: + return *e + case *HeartbeatEvent: + return *e + case *SourceOrderChangedEvent: + return *e + case *SceneItemAddedEvent: + return *e + case *SceneItemRemovedEvent: + return *e + case *SceneItemVisibilityChangedEvent: + return *e + case *PreviewSceneChangedEvent: + return *e + case *StudioModeSwitchedEvent: + return *e + default: + return nil + } +} diff --git a/obsws/events.go b/obsws/events.go new file mode 100644 index 0000000..4c28eeb --- /dev/null +++ b/obsws/events.go @@ -0,0 +1,24 @@ +package obsws + +// Event is broadcast by the server to each connected client when a recognized action occurs within OBS. +type Event interface { + Type() string + StreamTimecode() string + RecTimecode() string +} + +// https://github.com/Palakis/obs-websocket/blob/master/docs/generated/protocol.md#events +type _event struct { + Type_ string `json:"update-type"` + StreamTimecode_ string `json:"stream-timecode"` + RecTimecode_ string `json:"rec-timecode"` +} + +// Type returns the event's update type. +func (e _event) Type() string { return e.Type_ } + +// StreamTimeode returns the event's stream timecode. +func (e _event) StreamTimecode() string { return e.StreamTimecode_ } + +// RecTimecode returns the event's recording timecode. +func (e _event) RecTimecode() string { return e.RecTimecode_ } diff --git a/obsws/events_general.go b/obsws/events_general.go new file mode 100644 index 0000000..f32ee30 --- /dev/null +++ b/obsws/events_general.go @@ -0,0 +1,44 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// HeartbeatEvent : Emitted every 2 seconds after enabling it by calling SetHeartbeat. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#heartbeat +type HeartbeatEvent struct { + // Toggles between every JSON message as an "I am alive" indicator. + // Required: Yes. + Pulse bool `json:"pulse"` + // Current active profile. + // Required: No. + CurrentProfile string `json:"current-profile"` + // Current active scene. + // Required: No. + CurrentScene string `json:"current-scene"` + // Current streaming state. + // Required: No. + Streaming bool `json:"streaming"` + // Total time (in seconds) since the stream started. + // Required: No. + TotalStreamTime int `json:"total-stream-time"` + // Total bytes sent since the stream started. + // Required: No. + TotalStreamBytes int `json:"total-stream-bytes"` + // Total frames streamed since the stream started. + // Required: No. + TotalStreamFrames int `json:"total-stream-frames"` + // Current recording state. + // Required: No. + Recording bool `json:"recording"` + // Total time (in seconds) since recording started. + // Required: No. + TotalRecordTime int `json:"total-record-time"` + // Total bytes recorded since the recording started. + // Required: No. + TotalRecordBytes int `json:"total-record-bytes"` + // Total frames recorded since the recording started. + // Required: No. + TotalRecordFrames int `json:"total-record-frames"` + _event `json:",squash"` +} diff --git a/obsws/events_other.go b/obsws/events_other.go new file mode 100644 index 0000000..667b2cd --- /dev/null +++ b/obsws/events_other.go @@ -0,0 +1,13 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// ExitingEvent : OBS is exiting. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#exiting +type ExitingEvent struct { + _event `json:",squash"` +} diff --git a/obsws/events_profiles.go b/obsws/events_profiles.go new file mode 100644 index 0000000..3d8ab9e --- /dev/null +++ b/obsws/events_profiles.go @@ -0,0 +1,22 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// ProfileChangedEvent : Triggered when switching to another profile or when renaming the current profile. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#profilechanged +type ProfileChangedEvent struct { + _event `json:",squash"` +} + +// ProfileListChangedEvent : Triggered when a profile is created, added, renamed, or removed. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#profilelistchanged +type ProfileListChangedEvent struct { + _event `json:",squash"` +} diff --git a/obsws/events_recording.go b/obsws/events_recording.go new file mode 100644 index 0000000..a7702f8 --- /dev/null +++ b/obsws/events_recording.go @@ -0,0 +1,40 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// RecordingStartingEvent : A request to start recording has been issued. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#recordingstarting +type RecordingStartingEvent struct { + _event `json:",squash"` +} + +// RecordingStartedEvent : Recording started successfully. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#recordingstarted +type RecordingStartedEvent struct { + _event `json:",squash"` +} + +// RecordingStoppingEvent : A request to stop recording has been issued. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#recordingstopping +type RecordingStoppingEvent struct { + _event `json:",squash"` +} + +// RecordingStoppedEvent : Recording stopped successfully. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#recordingstopped +type RecordingStoppedEvent struct { + _event `json:",squash"` +} diff --git a/obsws/events_replay_buffer.go b/obsws/events_replay_buffer.go new file mode 100644 index 0000000..57a6948 --- /dev/null +++ b/obsws/events_replay_buffer.go @@ -0,0 +1,40 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// ReplayStartingEvent : A request to start the replay buffer has been issued. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#replaystarting +type ReplayStartingEvent struct { + _event `json:",squash"` +} + +// ReplayStartedEvent : Replay Buffer started successfully. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#replaystarted +type ReplayStartedEvent struct { + _event `json:",squash"` +} + +// ReplayStoppingEvent : A request to stop the replay buffer has been issued. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#replaystopping +type ReplayStoppingEvent struct { + _event `json:",squash"` +} + +// ReplayStoppedEvent : Replay Buffer stopped successfully. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#replaystopped +type ReplayStoppedEvent struct { + _event `json:",squash"` +} diff --git a/obsws/events_scenes.go b/obsws/events_scenes.go new file mode 100644 index 0000000..ee49469 --- /dev/null +++ b/obsws/events_scenes.go @@ -0,0 +1,47 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// SwitchScenesEvent : Indicates a scene change. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#switchscenes +type SwitchScenesEvent struct { + // The new scene. + // Required: Yes. + SceneName string `json:"scene-name"` + // List of sources in the new scene. + // Required: Yes. + Sources []interface{} `json:"sources"` + _event `json:",squash"` +} + +// ScenesChangedEvent : The scene list has been modified. +// Scenes have been added, removed, or renamed. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#sceneschanged +type ScenesChangedEvent struct { + _event `json:",squash"` +} + +// SceneCollectionChangedEvent : Triggered when switching to another scene collection or when renaming the current scene collection. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#scenecollectionchanged +type SceneCollectionChangedEvent struct { + _event `json:",squash"` +} + +// SceneCollectionListChangedEvent : Triggered when a scene collection is created, added, renamed, or removed. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#scenecollectionlistchanged +type SceneCollectionListChangedEvent struct { + _event `json:",squash"` +} diff --git a/obsws/events_sources.go b/obsws/events_sources.go new file mode 100644 index 0000000..ac23596 --- /dev/null +++ b/obsws/events_sources.go @@ -0,0 +1,64 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// SourceOrderChangedEvent : Scene items have been reordered. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#sourceorderchanged +type SourceOrderChangedEvent struct { + // Name of the scene where items have been reordered. + // Required: Yes. + SceneName string `json:"scene-name"` + _event `json:",squash"` +} + +// SceneItemAddedEvent : An item has been added to the current scene. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#sceneitemadded +type SceneItemAddedEvent struct { + // Name of the scene. + // Required: Yes. + SceneName string `json:"scene-name"` + // Name of the item added to the scene. + // Required: Yes. + ItemName string `json:"item-name"` + _event `json:",squash"` +} + +// SceneItemRemovedEvent : An item has been removed from the current scene. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#sceneitemremoved +type SceneItemRemovedEvent struct { + // Name of the scene. + // Required: Yes. + SceneName string `json:"scene-name"` + // Name of the item removed from the scene. + // Required: Yes. + ItemName string `json:"item-name"` + _event `json:",squash"` +} + +// SceneItemVisibilityChangedEvent : An item's visibility has been toggled. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#sceneitemvisibilitychanged +type SceneItemVisibilityChangedEvent struct { + // Name of the scene. + // Required: Yes. + SceneName string `json:"scene-name"` + // Name of the item in the scene. + // Required: Yes. + ItemName string `json:"item-name"` + // New visibility state of the item. + // Required: Yes. + ItemVisible bool `json:"item-visible"` + _event `json:",squash"` +} diff --git a/obsws/events_streaming.go b/obsws/events_streaming.go new file mode 100644 index 0000000..27744c1 --- /dev/null +++ b/obsws/events_streaming.go @@ -0,0 +1,88 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// StreamStartingEvent : A request to start streaming has been issued. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#streamstarting +type StreamStartingEvent struct { + // Always false (retrocompatibility). + // Required: Yes. + PreviewOnly bool `json:"preview-only"` + _event `json:",squash"` +} + +// StreamStartedEvent : Streaming started successfully. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#streamstarted +type StreamStartedEvent struct { + _event `json:",squash"` +} + +// StreamStoppingEvent : A request to stop streaming has been issued. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#streamstopping +type StreamStoppingEvent struct { + // Always false (retrocompatibility). + // Required: Yes. + PreviewOnly bool `json:"preview-only"` + _event `json:",squash"` +} + +// StreamStoppedEvent : Streaming stopped successfully. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#streamstopped +type StreamStoppedEvent struct { + _event `json:",squash"` +} + +// StreamStatusEvent : Emit every 2 seconds. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#streamstatus +type StreamStatusEvent struct { + // Current streaming state. + // Required: Yes. + Streaming bool `json:"streaming"` + // Current recording state. + // Required: Yes. + Recording bool `json:"recording"` + // Current replay state. + // Required: Yes. + Replay bool `json:"replay-buffer-active"` + // Always false (retrocompatibility). + // Required: Yes. + PreviewOnly bool `json:"preview-only"` + // Amount of data per second (in bytes) transmitted by the stream encoder. + // Required: Yes. + BytesPerSec int `json:"bytes-per-sec"` + // Amount of data per second (in kilobits) transmitted by the stream encoder. + // Required: Yes. + KbitsPerSec int `json:"kbits-per-sec"` + // Percentage of dropped frames. + // Required: Yes. + Strain float64 `json:"strain"` + // Total time (in seconds) since the stream started. + // Required: Yes. + TotalStreamTime int `json:"total-stream-time"` + // Total number of frames transmitted since the stream started. + // Required: Yes. + NumTotalFrames int `json:"num-total-frames"` + // Number of frames dropped by the encoder since the stream started. + // Required: Yes. + NumDroppedFrames int `json:"num-dropped-frames"` + // Current framerate. + // Required: Yes. + FPS float64 `json:"fps"` + _event `json:",squash"` +} diff --git a/obsws/events_studio_mode.go b/obsws/events_studio_mode.go new file mode 100644 index 0000000..80182d1 --- /dev/null +++ b/obsws/events_studio_mode.go @@ -0,0 +1,32 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// PreviewSceneChangedEvent : The selected preview scene has changed (only available in Studio Mode). +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#previewscenechanged +type PreviewSceneChangedEvent struct { + // Name of the scene being previewed. + // Required: Yes. + SceneName string `json:"scene-name"` + // List of sources composing the scene. + // Same specification as [`GetCurrentScene`](#getcurrentscene). + // Required: Yes. + Sources []map[string]interface{} `json:"sources"` + _event `json:",squash"` +} + +// StudioModeSwitchedEvent : Studio Mode has been enabled or disabled. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#studiomodeswitched +type StudioModeSwitchedEvent struct { + // The new enabled state of Studio Mode. + // Required: Yes. + NewState bool `json:"new-state"` + _event `json:",squash"` +} diff --git a/obsws/events_transitions.go b/obsws/events_transitions.go new file mode 100644 index 0000000..2e7e349 --- /dev/null +++ b/obsws/events_transitions.go @@ -0,0 +1,59 @@ +package obsws + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// SwitchTransitionEvent : The active transition has been changed. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#switchtransition +type SwitchTransitionEvent struct { + // The name of the new active transition. + // Required: Yes. + TransitionName string `json:"transition-name"` + _event `json:",squash"` +} + +// TransitionListChangedEvent : The list of available transitions has been modified. +// Transitions have been added, removed, or renamed. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#transitionlistchanged +type TransitionListChangedEvent struct { + _event `json:",squash"` +} + +// TransitionDurationChangedEvent : The active transition duration has been changed. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#transitiondurationchanged +type TransitionDurationChangedEvent struct { + // New transition duration. + // Required: Yes. + NewDuration int `json:"new-duration"` + _event `json:",squash"` +} + +// TransitionBeginEvent : A transition (other than "cut") has begun. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#transitionbegin +type TransitionBeginEvent struct { + // Transition name. + // Required: Yes. + Name string `json:"name"` + // Transition duration (in milliseconds). + // Required: Yes. + Duration int `json:"duration"` + // Source scene of the transition. + // Required: Yes. + FromScene string `json:"from-scene"` + // Destination scene of the transition. + // Required: Yes. + ToScene string `json:"to-scene"` + _event `json:",squash"` +} diff --git a/obsws/go.sum b/obsws/go.sum new file mode 100644 index 0000000..6f55a23 --- /dev/null +++ b/obsws/go.sum @@ -0,0 +1,4 @@ +github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= diff --git a/obsws/logger.go b/obsws/logger.go new file mode 100644 index 0000000..e8031ef --- /dev/null +++ b/obsws/logger.go @@ -0,0 +1,8 @@ +package obsws + +import ( + "log" + "os" +) + +var Logger = log.New(os.Stdout, "[obsws] ", log.LstdFlags) diff --git a/obsws/requests.go b/obsws/requests.go new file mode 100644 index 0000000..d537d57 --- /dev/null +++ b/obsws/requests.go @@ -0,0 +1,65 @@ +package obsws + +import "errors" + +const ( + // StatusOK indicates that the request was successful. + StatusOK = "ok" + // StatusError indicates that the request was unsuccessful. + StatusError = "error" +) + +var ( + // ErrNotSent is returned when you call Receive on a request that has not been sent. + ErrNotSent = errors.New("request not yet sent") + // ErrAlreadySent is returned when a request has already been sent. + ErrAlreadySent = errors.New("request already sent") +) + +// Request is a request to obs-websocket. +type Request interface { + ID() string + Type() string + Send(*Client) error +} + +// Response is a response from obs-websocket. +type Response interface { + ID() string + Status() string + Error() string +} + +// https://github.com/Palakis/obs-websocket/blob/master/docs/generated/protocol.md#requests +type _request struct { + ID_ string `json:"message-id"` + Type_ string `json:"request-type"` + sent bool + err chan error +} + +func (r _request) Send(c *Client) error { return nil } + +// ID returns the request's message ID. +func (r _request) ID() string { return r.ID_ } + +// Type returns the request's message type. +func (r _request) Type() string { return r.Type_ } + +// https://github.com/Palakis/obs-websocket/blob/master/docs/generated/protocol.md#requests +type _response struct { + ID_ string `json:"message-id"` + Status_ string `json:"status"` + Error_ string `json:"error"` +} + +// ID returns the response's message ID. +func (r _response) ID() string { return r.ID_ } + +// Status returns the response's status. +func (r _response) Status() string { return r.Status_ } + +// Error returns the response's error. +// When using Receive or SendReceive, this should always return an empty string, +// because the error will have been returned explictly instead of stored here. +func (r _response) Error() string { return r.Error_ } diff --git a/obsws/requests_general.go b/obsws/requests_general.go new file mode 100644 index 0000000..8a48db8 --- /dev/null +++ b/obsws/requests_general.go @@ -0,0 +1,568 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// GetVersionRequest : Returns the latest version of the plugin and the API. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getversion +type GetVersionRequest struct { + _request `json:",squash"` + response chan GetVersionResponse +} + +// NewGetVersionRequest returns a new GetVersionRequest. +func NewGetVersionRequest() GetVersionRequest { + return GetVersionRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetVersion", + err: make(chan error, 1), + }, + make(chan GetVersionResponse, 1), + } +} + +// Send sends the request. +func (r *GetVersionRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetVersionResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetVersionRequest) Receive() (GetVersionResponse, error) { + if !r.sent { + return GetVersionResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetVersionResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetVersionResponse{}, err + case <-time.After(receiveTimeout): + return GetVersionResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetVersionRequest) SendReceive(c *Client) (GetVersionResponse, error) { + if err := r.Send(c); err != nil { + return GetVersionResponse{}, err + } + return r.Receive() +} + +// GetVersionResponse : Response for GetVersionRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getversion +type GetVersionResponse struct { + // OBSRemote compatible API version. + // Fixed to 1.1 for retrocompatibility. + // Required: Yes. + Version float64 `json:"version"` + // obs-websocket plugin version. + // Required: Yes. + OBSWebsocketVersion string `json:"obs-websocket-version"` + // OBS Studio program version. + // Required: Yes. + OBSStudioVersion string `json:"obs-studio-version"` + // List of available request types, formatted as a comma-separated list string (e.g. : "Method1,Method2,Method3"). + // Required: Yes. + AvailableRequests string `json:"available-requests"` + _response `json:",squash"` +} + +// GetAuthRequiredRequest : Tells the client if authentication is required +// If so, returns authentication parameters `challenge` +// and `salt` (see "Authentication" for more information). +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getauthrequired +type GetAuthRequiredRequest struct { + _request `json:",squash"` + response chan GetAuthRequiredResponse +} + +// NewGetAuthRequiredRequest returns a new GetAuthRequiredRequest. +func NewGetAuthRequiredRequest() GetAuthRequiredRequest { + return GetAuthRequiredRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetAuthRequired", + err: make(chan error, 1), + }, + make(chan GetAuthRequiredResponse, 1), + } +} + +// Send sends the request. +func (r *GetAuthRequiredRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetAuthRequiredResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetAuthRequiredRequest) Receive() (GetAuthRequiredResponse, error) { + if !r.sent { + return GetAuthRequiredResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetAuthRequiredResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetAuthRequiredResponse{}, err + case <-time.After(receiveTimeout): + return GetAuthRequiredResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetAuthRequiredRequest) SendReceive(c *Client) (GetAuthRequiredResponse, error) { + if err := r.Send(c); err != nil { + return GetAuthRequiredResponse{}, err + } + return r.Receive() +} + +// GetAuthRequiredResponse : Response for GetAuthRequiredRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getauthrequired +type GetAuthRequiredResponse struct { + // Indicates whether authentication is required. + // Required: Yes. + AuthRequired bool `json:"authRequired"` + // Required: No. + Challenge string `json:"challenge"` + // Required: No. + Salt string `json:"salt"` + _response `json:",squash"` +} + +// AuthenticateRequest : Attempt to authenticate the client to the server. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#authenticate +type AuthenticateRequest struct { + // Response to the auth challenge (see "Authentication" for more information). + // Required: Yes. + Auth string `json:"auth"` + _request `json:",squash"` + response chan AuthenticateResponse +} + +// NewAuthenticateRequest returns a new AuthenticateRequest. +func NewAuthenticateRequest(auth string) AuthenticateRequest { + return AuthenticateRequest{ + auth, + _request{ + ID_: getMessageID(), + Type_: "Authenticate", + err: make(chan error, 1), + }, + make(chan AuthenticateResponse, 1), + } +} + +// Send sends the request. +func (r *AuthenticateRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp AuthenticateResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r AuthenticateRequest) Receive() (AuthenticateResponse, error) { + if !r.sent { + return AuthenticateResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return AuthenticateResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return AuthenticateResponse{}, err + case <-time.After(receiveTimeout): + return AuthenticateResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r AuthenticateRequest) SendReceive(c *Client) (AuthenticateResponse, error) { + if err := r.Send(c); err != nil { + return AuthenticateResponse{}, err + } + return r.Receive() +} + +// AuthenticateResponse : Response for AuthenticateRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#authenticate +type AuthenticateResponse struct { + _response `json:",squash"` +} + +// SetHeartbeatRequest : Enable/disable sending of the Heartbeat event. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setheartbeat +type SetHeartbeatRequest struct { + // Starts/Stops emitting heartbeat messages. + // Required: Yes. + Enable bool `json:"enable"` + _request `json:",squash"` + response chan SetHeartbeatResponse +} + +// NewSetHeartbeatRequest returns a new SetHeartbeatRequest. +func NewSetHeartbeatRequest(enable bool) SetHeartbeatRequest { + return SetHeartbeatRequest{ + enable, + _request{ + ID_: getMessageID(), + Type_: "SetHeartbeat", + err: make(chan error, 1), + }, + make(chan SetHeartbeatResponse, 1), + } +} + +// Send sends the request. +func (r *SetHeartbeatRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetHeartbeatResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetHeartbeatRequest) Receive() (SetHeartbeatResponse, error) { + if !r.sent { + return SetHeartbeatResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetHeartbeatResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetHeartbeatResponse{}, err + case <-time.After(receiveTimeout): + return SetHeartbeatResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetHeartbeatRequest) SendReceive(c *Client) (SetHeartbeatResponse, error) { + if err := r.Send(c); err != nil { + return SetHeartbeatResponse{}, err + } + return r.Receive() +} + +// SetHeartbeatResponse : Response for SetHeartbeatRequest. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setheartbeat +type SetHeartbeatResponse struct { + _response `json:",squash"` +} + +// SetFilenameFormattingRequest : Set the filename formatting string. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setfilenameformatting +type SetFilenameFormattingRequest struct { + // Filename formatting string to set. + // Required: Yes. + FilenameFormatting string `json:"filename-formatting"` + _request `json:",squash"` + response chan SetFilenameFormattingResponse +} + +// NewSetFilenameFormattingRequest returns a new SetFilenameFormattingRequest. +func NewSetFilenameFormattingRequest(filenameFormatting string) SetFilenameFormattingRequest { + return SetFilenameFormattingRequest{ + filenameFormatting, + _request{ + ID_: getMessageID(), + Type_: "SetFilenameFormatting", + err: make(chan error, 1), + }, + make(chan SetFilenameFormattingResponse, 1), + } +} + +// Send sends the request. +func (r *SetFilenameFormattingRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetFilenameFormattingResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetFilenameFormattingRequest) Receive() (SetFilenameFormattingResponse, error) { + if !r.sent { + return SetFilenameFormattingResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetFilenameFormattingResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetFilenameFormattingResponse{}, err + case <-time.After(receiveTimeout): + return SetFilenameFormattingResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetFilenameFormattingRequest) SendReceive(c *Client) (SetFilenameFormattingResponse, error) { + if err := r.Send(c); err != nil { + return SetFilenameFormattingResponse{}, err + } + return r.Receive() +} + +// SetFilenameFormattingResponse : Response for SetFilenameFormattingRequest. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setfilenameformatting +type SetFilenameFormattingResponse struct { + _response `json:",squash"` +} + +// GetFilenameFormattingRequest : Get the filename formatting string. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getfilenameformatting +type GetFilenameFormattingRequest struct { + _request `json:",squash"` + response chan GetFilenameFormattingResponse +} + +// NewGetFilenameFormattingRequest returns a new GetFilenameFormattingRequest. +func NewGetFilenameFormattingRequest() GetFilenameFormattingRequest { + return GetFilenameFormattingRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetFilenameFormatting", + err: make(chan error, 1), + }, + make(chan GetFilenameFormattingResponse, 1), + } +} + +// Send sends the request. +func (r *GetFilenameFormattingRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetFilenameFormattingResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetFilenameFormattingRequest) Receive() (GetFilenameFormattingResponse, error) { + if !r.sent { + return GetFilenameFormattingResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetFilenameFormattingResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetFilenameFormattingResponse{}, err + case <-time.After(receiveTimeout): + return GetFilenameFormattingResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetFilenameFormattingRequest) SendReceive(c *Client) (GetFilenameFormattingResponse, error) { + if err := r.Send(c); err != nil { + return GetFilenameFormattingResponse{}, err + } + return r.Receive() +} + +// GetFilenameFormattingResponse : Response for GetFilenameFormattingRequest. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getfilenameformatting +type GetFilenameFormattingResponse struct { + // Current filename formatting string. + // Required: Yes. + FilenameFormatting string `json:"filename-formatting"` + _response `json:",squash"` +} diff --git a/obsws/requests_profiles.go b/obsws/requests_profiles.go new file mode 100644 index 0000000..8a7f1b9 --- /dev/null +++ b/obsws/requests_profiles.go @@ -0,0 +1,280 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// SetCurrentProfileRequest : Set the currently active profile. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setcurrentprofile +type SetCurrentProfileRequest struct { + // Name of the desired profile. + // Required: Yes. + ProfileName string `json:"profile-name"` + _request `json:",squash"` + response chan SetCurrentProfileResponse +} + +// NewSetCurrentProfileRequest returns a new SetCurrentProfileRequest. +func NewSetCurrentProfileRequest(profileName string) SetCurrentProfileRequest { + return SetCurrentProfileRequest{ + profileName, + _request{ + ID_: getMessageID(), + Type_: "SetCurrentProfile", + err: make(chan error, 1), + }, + make(chan SetCurrentProfileResponse, 1), + } +} + +// Send sends the request. +func (r *SetCurrentProfileRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetCurrentProfileResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetCurrentProfileRequest) Receive() (SetCurrentProfileResponse, error) { + if !r.sent { + return SetCurrentProfileResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetCurrentProfileResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetCurrentProfileResponse{}, err + case <-time.After(receiveTimeout): + return SetCurrentProfileResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetCurrentProfileRequest) SendReceive(c *Client) (SetCurrentProfileResponse, error) { + if err := r.Send(c); err != nil { + return SetCurrentProfileResponse{}, err + } + return r.Receive() +} + +// SetCurrentProfileResponse : Response for SetCurrentProfileRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setcurrentprofile +type SetCurrentProfileResponse struct { + _response `json:",squash"` +} + +// GetCurrentProfileRequest : Get the name of the current profile. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getcurrentprofile +type GetCurrentProfileRequest struct { + _request `json:",squash"` + response chan GetCurrentProfileResponse +} + +// NewGetCurrentProfileRequest returns a new GetCurrentProfileRequest. +func NewGetCurrentProfileRequest() GetCurrentProfileRequest { + return GetCurrentProfileRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetCurrentProfile", + err: make(chan error, 1), + }, + make(chan GetCurrentProfileResponse, 1), + } +} + +// Send sends the request. +func (r *GetCurrentProfileRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetCurrentProfileResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetCurrentProfileRequest) Receive() (GetCurrentProfileResponse, error) { + if !r.sent { + return GetCurrentProfileResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetCurrentProfileResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetCurrentProfileResponse{}, err + case <-time.After(receiveTimeout): + return GetCurrentProfileResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetCurrentProfileRequest) SendReceive(c *Client) (GetCurrentProfileResponse, error) { + if err := r.Send(c); err != nil { + return GetCurrentProfileResponse{}, err + } + return r.Receive() +} + +// GetCurrentProfileResponse : Response for GetCurrentProfileRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getcurrentprofile +type GetCurrentProfileResponse struct { + // Name of the currently active profile. + // Required: Yes. + ProfileName string `json:"profile-name"` + _response `json:",squash"` +} + +// ListProfilesRequest : Get a list of available profiles. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#listprofiles +type ListProfilesRequest struct { + _request `json:",squash"` + response chan ListProfilesResponse +} + +// NewListProfilesRequest returns a new ListProfilesRequest. +func NewListProfilesRequest() ListProfilesRequest { + return ListProfilesRequest{ + _request{ + ID_: getMessageID(), + Type_: "ListProfiles", + err: make(chan error, 1), + }, + make(chan ListProfilesResponse, 1), + } +} + +// Send sends the request. +func (r *ListProfilesRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp ListProfilesResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r ListProfilesRequest) Receive() (ListProfilesResponse, error) { + if !r.sent { + return ListProfilesResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ListProfilesResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ListProfilesResponse{}, err + case <-time.After(receiveTimeout): + return ListProfilesResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r ListProfilesRequest) SendReceive(c *Client) (ListProfilesResponse, error) { + if err := r.Send(c); err != nil { + return ListProfilesResponse{}, err + } + return r.Receive() +} + +// ListProfilesResponse : Response for ListProfilesRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#listprofiles +type ListProfilesResponse struct { + // List of available profiles. + // Required: Yes. + Profiles interface{} `json:"profiles"` + _response `json:",squash"` +} diff --git a/obsws/requests_recording.go b/obsws/requests_recording.go new file mode 100644 index 0000000..784675b --- /dev/null +++ b/obsws/requests_recording.go @@ -0,0 +1,453 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// StartStopRecordingRequest : Toggle recording on or off. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startstoprecording +type StartStopRecordingRequest struct { + _request `json:",squash"` + response chan StartStopRecordingResponse +} + +// NewStartStopRecordingRequest returns a new StartStopRecordingRequest. +func NewStartStopRecordingRequest() StartStopRecordingRequest { + return StartStopRecordingRequest{ + _request{ + ID_: getMessageID(), + Type_: "StartStopRecording", + err: make(chan error, 1), + }, + make(chan StartStopRecordingResponse, 1), + } +} + +// Send sends the request. +func (r *StartStopRecordingRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp StartStopRecordingResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r StartStopRecordingRequest) Receive() (StartStopRecordingResponse, error) { + if !r.sent { + return StartStopRecordingResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartStopRecordingResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartStopRecordingResponse{}, err + case <-time.After(receiveTimeout): + return StartStopRecordingResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r StartStopRecordingRequest) SendReceive(c *Client) (StartStopRecordingResponse, error) { + if err := r.Send(c); err != nil { + return StartStopRecordingResponse{}, err + } + return r.Receive() +} + +// StartStopRecordingResponse : Response for StartStopRecordingRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startstoprecording +type StartStopRecordingResponse struct { + _response `json:",squash"` +} + +// StartRecordingRequest : Start recording. +// Will return an `error` if recording is already active. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startrecording +type StartRecordingRequest struct { + _request `json:",squash"` + response chan StartRecordingResponse +} + +// NewStartRecordingRequest returns a new StartRecordingRequest. +func NewStartRecordingRequest() StartRecordingRequest { + return StartRecordingRequest{ + _request{ + ID_: getMessageID(), + Type_: "StartRecording", + err: make(chan error, 1), + }, + make(chan StartRecordingResponse, 1), + } +} + +// Send sends the request. +func (r *StartRecordingRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp StartRecordingResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r StartRecordingRequest) Receive() (StartRecordingResponse, error) { + if !r.sent { + return StartRecordingResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartRecordingResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartRecordingResponse{}, err + case <-time.After(receiveTimeout): + return StartRecordingResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r StartRecordingRequest) SendReceive(c *Client) (StartRecordingResponse, error) { + if err := r.Send(c); err != nil { + return StartRecordingResponse{}, err + } + return r.Receive() +} + +// StartRecordingResponse : Response for StartRecordingRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startrecording +type StartRecordingResponse struct { + _response `json:",squash"` +} + +// StopRecordingRequest : Stop recording. +// Will return an `error` if recording is not active. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#stoprecording +type StopRecordingRequest struct { + _request `json:",squash"` + response chan StopRecordingResponse +} + +// NewStopRecordingRequest returns a new StopRecordingRequest. +func NewStopRecordingRequest() StopRecordingRequest { + return StopRecordingRequest{ + _request{ + ID_: getMessageID(), + Type_: "StopRecording", + err: make(chan error, 1), + }, + make(chan StopRecordingResponse, 1), + } +} + +// Send sends the request. +func (r *StopRecordingRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp StopRecordingResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r StopRecordingRequest) Receive() (StopRecordingResponse, error) { + if !r.sent { + return StopRecordingResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StopRecordingResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StopRecordingResponse{}, err + case <-time.After(receiveTimeout): + return StopRecordingResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r StopRecordingRequest) SendReceive(c *Client) (StopRecordingResponse, error) { + if err := r.Send(c); err != nil { + return StopRecordingResponse{}, err + } + return r.Receive() +} + +// StopRecordingResponse : Response for StopRecordingRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#stoprecording +type StopRecordingResponse struct { + _response `json:",squash"` +} + +// SetRecordingFolderRequest : Change the current recording folder. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setrecordingfolder +type SetRecordingFolderRequest struct { + // Path of the recording folder. + // Required: Yes. + RecFolder string `json:"rec-folder"` + _request `json:",squash"` + response chan SetRecordingFolderResponse +} + +// NewSetRecordingFolderRequest returns a new SetRecordingFolderRequest. +func NewSetRecordingFolderRequest(recFolder string) SetRecordingFolderRequest { + return SetRecordingFolderRequest{ + recFolder, + _request{ + ID_: getMessageID(), + Type_: "SetRecordingFolder", + err: make(chan error, 1), + }, + make(chan SetRecordingFolderResponse, 1), + } +} + +// Send sends the request. +func (r *SetRecordingFolderRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetRecordingFolderResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetRecordingFolderRequest) Receive() (SetRecordingFolderResponse, error) { + if !r.sent { + return SetRecordingFolderResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetRecordingFolderResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetRecordingFolderResponse{}, err + case <-time.After(receiveTimeout): + return SetRecordingFolderResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetRecordingFolderRequest) SendReceive(c *Client) (SetRecordingFolderResponse, error) { + if err := r.Send(c); err != nil { + return SetRecordingFolderResponse{}, err + } + return r.Receive() +} + +// SetRecordingFolderResponse : Response for SetRecordingFolderRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setrecordingfolder +type SetRecordingFolderResponse struct { + _response `json:",squash"` +} + +// GetRecordingFolderRequest : Get the path of the current recording folder. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getrecordingfolder +type GetRecordingFolderRequest struct { + _request `json:",squash"` + response chan GetRecordingFolderResponse +} + +// NewGetRecordingFolderRequest returns a new GetRecordingFolderRequest. +func NewGetRecordingFolderRequest() GetRecordingFolderRequest { + return GetRecordingFolderRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetRecordingFolder", + err: make(chan error, 1), + }, + make(chan GetRecordingFolderResponse, 1), + } +} + +// Send sends the request. +func (r *GetRecordingFolderRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetRecordingFolderResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetRecordingFolderRequest) Receive() (GetRecordingFolderResponse, error) { + if !r.sent { + return GetRecordingFolderResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetRecordingFolderResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetRecordingFolderResponse{}, err + case <-time.After(receiveTimeout): + return GetRecordingFolderResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetRecordingFolderRequest) SendReceive(c *Client) (GetRecordingFolderResponse, error) { + if err := r.Send(c); err != nil { + return GetRecordingFolderResponse{}, err + } + return r.Receive() +} + +// GetRecordingFolderResponse : Response for GetRecordingFolderRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getrecordingfolder +type GetRecordingFolderResponse struct { + // Path of the recording folder. + // Required: Yes. + RecFolder string `json:"rec-folder"` + _response `json:",squash"` +} diff --git a/obsws/requests_replay_buffer.go b/obsws/requests_replay_buffer.go new file mode 100644 index 0000000..ab32ed1 --- /dev/null +++ b/obsws/requests_replay_buffer.go @@ -0,0 +1,365 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// StartStopReplayBufferRequest : Toggle the Replay Buffer on/off. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startstopreplaybuffer +type StartStopReplayBufferRequest struct { + _request `json:",squash"` + response chan StartStopReplayBufferResponse +} + +// NewStartStopReplayBufferRequest returns a new StartStopReplayBufferRequest. +func NewStartStopReplayBufferRequest() StartStopReplayBufferRequest { + return StartStopReplayBufferRequest{ + _request{ + ID_: getMessageID(), + Type_: "StartStopReplayBuffer", + err: make(chan error, 1), + }, + make(chan StartStopReplayBufferResponse, 1), + } +} + +// Send sends the request. +func (r *StartStopReplayBufferRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp StartStopReplayBufferResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r StartStopReplayBufferRequest) Receive() (StartStopReplayBufferResponse, error) { + if !r.sent { + return StartStopReplayBufferResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartStopReplayBufferResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartStopReplayBufferResponse{}, err + case <-time.After(receiveTimeout): + return StartStopReplayBufferResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r StartStopReplayBufferRequest) SendReceive(c *Client) (StartStopReplayBufferResponse, error) { + if err := r.Send(c); err != nil { + return StartStopReplayBufferResponse{}, err + } + return r.Receive() +} + +// StartStopReplayBufferResponse : Response for StartStopReplayBufferRequest. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startstopreplaybuffer +type StartStopReplayBufferResponse struct { + _response `json:",squash"` +} + +// StartReplayBufferRequest : Start recording into the Replay Buffer. +// Will return an `error` if the Replay Buffer is already active or if the +// "Save Replay Buffer" hotkey is not set in OBS' settings. +// Setting this hotkey is mandatory, even when triggering saves only +// through obs-websocket. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startreplaybuffer +type StartReplayBufferRequest struct { + _request `json:",squash"` + response chan StartReplayBufferResponse +} + +// NewStartReplayBufferRequest returns a new StartReplayBufferRequest. +func NewStartReplayBufferRequest() StartReplayBufferRequest { + return StartReplayBufferRequest{ + _request{ + ID_: getMessageID(), + Type_: "StartReplayBuffer", + err: make(chan error, 1), + }, + make(chan StartReplayBufferResponse, 1), + } +} + +// Send sends the request. +func (r *StartReplayBufferRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp StartReplayBufferResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r StartReplayBufferRequest) Receive() (StartReplayBufferResponse, error) { + if !r.sent { + return StartReplayBufferResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartReplayBufferResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartReplayBufferResponse{}, err + case <-time.After(receiveTimeout): + return StartReplayBufferResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r StartReplayBufferRequest) SendReceive(c *Client) (StartReplayBufferResponse, error) { + if err := r.Send(c); err != nil { + return StartReplayBufferResponse{}, err + } + return r.Receive() +} + +// StartReplayBufferResponse : Response for StartReplayBufferRequest. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startreplaybuffer +type StartReplayBufferResponse struct { + _response `json:",squash"` +} + +// StopReplayBufferRequest : Stop recording into the Replay Buffer. +// Will return an `error` if the Replay Buffer is not active. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#stopreplaybuffer +type StopReplayBufferRequest struct { + _request `json:",squash"` + response chan StopReplayBufferResponse +} + +// NewStopReplayBufferRequest returns a new StopReplayBufferRequest. +func NewStopReplayBufferRequest() StopReplayBufferRequest { + return StopReplayBufferRequest{ + _request{ + ID_: getMessageID(), + Type_: "StopReplayBuffer", + err: make(chan error, 1), + }, + make(chan StopReplayBufferResponse, 1), + } +} + +// Send sends the request. +func (r *StopReplayBufferRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp StopReplayBufferResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r StopReplayBufferRequest) Receive() (StopReplayBufferResponse, error) { + if !r.sent { + return StopReplayBufferResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StopReplayBufferResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StopReplayBufferResponse{}, err + case <-time.After(receiveTimeout): + return StopReplayBufferResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r StopReplayBufferRequest) SendReceive(c *Client) (StopReplayBufferResponse, error) { + if err := r.Send(c); err != nil { + return StopReplayBufferResponse{}, err + } + return r.Receive() +} + +// StopReplayBufferResponse : Response for StopReplayBufferRequest. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#stopreplaybuffer +type StopReplayBufferResponse struct { + _response `json:",squash"` +} + +// SaveReplayBufferRequest : Flush and save the contents of the Replay Buffer to disk +// This is +// basically the same as triggering the "Save Replay Buffer" hotkey. +// Will return an `error` if the Replay Buffer is not active. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#savereplaybuffer +type SaveReplayBufferRequest struct { + _request `json:",squash"` + response chan SaveReplayBufferResponse +} + +// NewSaveReplayBufferRequest returns a new SaveReplayBufferRequest. +func NewSaveReplayBufferRequest() SaveReplayBufferRequest { + return SaveReplayBufferRequest{ + _request{ + ID_: getMessageID(), + Type_: "SaveReplayBuffer", + err: make(chan error, 1), + }, + make(chan SaveReplayBufferResponse, 1), + } +} + +// Send sends the request. +func (r *SaveReplayBufferRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SaveReplayBufferResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SaveReplayBufferRequest) Receive() (SaveReplayBufferResponse, error) { + if !r.sent { + return SaveReplayBufferResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SaveReplayBufferResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SaveReplayBufferResponse{}, err + case <-time.After(receiveTimeout): + return SaveReplayBufferResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SaveReplayBufferRequest) SendReceive(c *Client) (SaveReplayBufferResponse, error) { + if err := r.Send(c); err != nil { + return SaveReplayBufferResponse{}, err + } + return r.Receive() +} + +// SaveReplayBufferResponse : Response for SaveReplayBufferRequest. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#savereplaybuffer +type SaveReplayBufferResponse struct { + _response `json:",squash"` +} diff --git a/obsws/requests_scene_collections.go b/obsws/requests_scene_collections.go new file mode 100644 index 0000000..b400ae7 --- /dev/null +++ b/obsws/requests_scene_collections.go @@ -0,0 +1,282 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// SetCurrentSceneCollectionRequest : Change the active scene collection. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setcurrentscenecollection +type SetCurrentSceneCollectionRequest struct { + // Name of the desired scene collection. + // Required: Yes. + ScName string `json:"sc-name"` + _request `json:",squash"` + response chan SetCurrentSceneCollectionResponse +} + +// NewSetCurrentSceneCollectionRequest returns a new SetCurrentSceneCollectionRequest. +func NewSetCurrentSceneCollectionRequest(scName string) SetCurrentSceneCollectionRequest { + return SetCurrentSceneCollectionRequest{ + scName, + _request{ + ID_: getMessageID(), + Type_: "SetCurrentSceneCollection", + err: make(chan error, 1), + }, + make(chan SetCurrentSceneCollectionResponse, 1), + } +} + +// Send sends the request. +func (r *SetCurrentSceneCollectionRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetCurrentSceneCollectionResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetCurrentSceneCollectionRequest) Receive() (SetCurrentSceneCollectionResponse, error) { + if !r.sent { + return SetCurrentSceneCollectionResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetCurrentSceneCollectionResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetCurrentSceneCollectionResponse{}, err + case <-time.After(receiveTimeout): + return SetCurrentSceneCollectionResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetCurrentSceneCollectionRequest) SendReceive(c *Client) (SetCurrentSceneCollectionResponse, error) { + if err := r.Send(c); err != nil { + return SetCurrentSceneCollectionResponse{}, err + } + return r.Receive() +} + +// SetCurrentSceneCollectionResponse : Response for SetCurrentSceneCollectionRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setcurrentscenecollection +type SetCurrentSceneCollectionResponse struct { + _response `json:",squash"` +} + +// GetCurrentSceneCollectionRequest : Get the name of the current scene collection. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getcurrentscenecollection +type GetCurrentSceneCollectionRequest struct { + _request `json:",squash"` + response chan GetCurrentSceneCollectionResponse +} + +// NewGetCurrentSceneCollectionRequest returns a new GetCurrentSceneCollectionRequest. +func NewGetCurrentSceneCollectionRequest() GetCurrentSceneCollectionRequest { + return GetCurrentSceneCollectionRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetCurrentSceneCollection", + err: make(chan error, 1), + }, + make(chan GetCurrentSceneCollectionResponse, 1), + } +} + +// Send sends the request. +func (r *GetCurrentSceneCollectionRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetCurrentSceneCollectionResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetCurrentSceneCollectionRequest) Receive() (GetCurrentSceneCollectionResponse, error) { + if !r.sent { + return GetCurrentSceneCollectionResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetCurrentSceneCollectionResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetCurrentSceneCollectionResponse{}, err + case <-time.After(receiveTimeout): + return GetCurrentSceneCollectionResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetCurrentSceneCollectionRequest) SendReceive(c *Client) (GetCurrentSceneCollectionResponse, error) { + if err := r.Send(c); err != nil { + return GetCurrentSceneCollectionResponse{}, err + } + return r.Receive() +} + +// GetCurrentSceneCollectionResponse : Response for GetCurrentSceneCollectionRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getcurrentscenecollection +type GetCurrentSceneCollectionResponse struct { + // Name of the currently active scene collection. + // Required: Yes. + ScName string `json:"sc-name"` + _response `json:",squash"` +} + +// ListSceneCollectionsRequest : List available scene collections. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#listscenecollections +type ListSceneCollectionsRequest struct { + _request `json:",squash"` + response chan ListSceneCollectionsResponse +} + +// NewListSceneCollectionsRequest returns a new ListSceneCollectionsRequest. +func NewListSceneCollectionsRequest() ListSceneCollectionsRequest { + return ListSceneCollectionsRequest{ + _request{ + ID_: getMessageID(), + Type_: "ListSceneCollections", + err: make(chan error, 1), + }, + make(chan ListSceneCollectionsResponse, 1), + } +} + +// Send sends the request. +func (r *ListSceneCollectionsRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp ListSceneCollectionsResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r ListSceneCollectionsRequest) Receive() (ListSceneCollectionsResponse, error) { + if !r.sent { + return ListSceneCollectionsResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ListSceneCollectionsResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ListSceneCollectionsResponse{}, err + case <-time.After(receiveTimeout): + return ListSceneCollectionsResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r ListSceneCollectionsRequest) SendReceive(c *Client) (ListSceneCollectionsResponse, error) { + if err := r.Send(c); err != nil { + return ListSceneCollectionsResponse{}, err + } + return r.Receive() +} + +// ListSceneCollectionsResponse : Response for ListSceneCollectionsRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#listscenecollections +type ListSceneCollectionsResponse struct { + // Scene collections list. + // Required: Yes. + SceneCollections interface{} `json:"scene-collections"` + // Required: Yes. + SceneCollectionsStar string `json:"scene-collections.*."` + _response `json:",squash"` +} diff --git a/obsws/requests_scene_items.go b/obsws/requests_scene_items.go new file mode 100644 index 0000000..8bed718 --- /dev/null +++ b/obsws/requests_scene_items.go @@ -0,0 +1,878 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// GetSceneItemPropertiesRequest : Gets the scene specific properties of the specified source item. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsceneitemproperties +type GetSceneItemPropertiesRequest struct { + // the name of the scene that the source item belongs to. + // Defaults to the current scene. + // Required: No. + SceneName string `json:"scene-name"` + // The name of the source. + // Required: Yes. + Item string `json:"item"` + _request `json:",squash"` + response chan GetSceneItemPropertiesResponse +} + +// NewGetSceneItemPropertiesRequest returns a new GetSceneItemPropertiesRequest. +func NewGetSceneItemPropertiesRequest( + sceneName string, + item string, +) GetSceneItemPropertiesRequest { + return GetSceneItemPropertiesRequest{ + sceneName, + item, + _request{ + ID_: getMessageID(), + Type_: "GetSceneItemProperties", + err: make(chan error, 1), + }, + make(chan GetSceneItemPropertiesResponse, 1), + } +} + +// Send sends the request. +func (r *GetSceneItemPropertiesRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetSceneItemPropertiesResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetSceneItemPropertiesRequest) Receive() (GetSceneItemPropertiesResponse, error) { + if !r.sent { + return GetSceneItemPropertiesResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSceneItemPropertiesResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSceneItemPropertiesResponse{}, err + case <-time.After(receiveTimeout): + return GetSceneItemPropertiesResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetSceneItemPropertiesRequest) SendReceive(c *Client) (GetSceneItemPropertiesResponse, error) { + if err := r.Send(c); err != nil { + return GetSceneItemPropertiesResponse{}, err + } + return r.Receive() +} + +// GetSceneItemPropertiesResponse : Response for GetSceneItemPropertiesRequest. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsceneitemproperties +type GetSceneItemPropertiesResponse struct { + // The name of the source. + // Required: Yes. + Name string `json:"name"` + // The x position of the source from the left. + // Required: Yes. + PositionX int `json:"position.x"` + // The y position of the source from the top. + // Required: Yes. + PositionY int `json:"position.y"` + // The point on the source that the item is manipulated from. + // Required: Yes. + PositionAlignment int `json:"position.alignment"` + // The clockwise rotation of the item in degrees around the point of alignment. + // Required: Yes. + Rotation float64 `json:"rotation"` + // The x-scale factor of the source. + // Required: Yes. + ScaleX float64 `json:"scale.x"` + // The y-scale factor of the source. + // Required: Yes. + ScaleY float64 `json:"scale.y"` + // The number of pixels cropped off the top of the source before scaling. + // Required: Yes. + CropTop int `json:"crop.top"` + // The number of pixels cropped off the right of the source before scaling. + // Required: Yes. + CropRight int `json:"crop.right"` + // The number of pixels cropped off the bottom of the source before scaling. + // Required: Yes. + CropBottom int `json:"crop.bottom"` + // The number of pixels cropped off the left of the source before scaling. + // Required: Yes. + CropLeft int `json:"crop.left"` + // If the source is visible. + // Required: Yes. + Visible bool `json:"visible"` + // Type of bounding box. + // Required: Yes. + BoundsType string `json:"bounds.type"` + // Alignment of the bounding box. + // Required: Yes. + BoundsAlignment int `json:"bounds.alignment"` + // Width of the bounding box. + // Required: Yes. + BoundsX float64 `json:"bounds.x"` + // Height of the bounding box. + // Required: Yes. + BoundsY float64 `json:"bounds.y"` + _response `json:",squash"` +} + +// SetSceneItemPropertiesRequest : Sets the scene specific properties of a source +// Unspecified properties will remain unchanged. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsceneitemproperties +type SetSceneItemPropertiesRequest struct { + // the name of the scene that the source item belongs to. + // Defaults to the current scene. + // Required: No. + SceneName string `json:"scene-name"` + // The name of the source. + // Required: Yes. + Item string `json:"item"` + // The new x position of the source. + // Required: Yes. + PositionX int `json:"position.x"` + // The new y position of the source. + // Required: Yes. + PositionY int `json:"position.y"` + // The new alignment of the source. + // Required: Yes. + PositionAlignment int `json:"position.alignment"` + // The new clockwise rotation of the item in degrees. + // Required: Yes. + Rotation float64 `json:"rotation"` + // The new x scale of the item. + // Required: Yes. + ScaleX float64 `json:"scale.x"` + // The new y scale of the item. + // Required: Yes. + ScaleY float64 `json:"scale.y"` + // The new amount of pixels cropped off the top of the source before scaling. + // Required: Yes. + CropTop int `json:"crop.top"` + // The new amount of pixels cropped off the bottom of the source before scaling. + // Required: Yes. + CropBottom int `json:"crop.bottom"` + // The new amount of pixels cropped off the left of the source before scaling. + // Required: Yes. + CropLeft int `json:"crop.left"` + // The new amount of pixels cropped off the right of the source before scaling. + // Required: Yes. + CropRight int `json:"crop.right"` + // The new visibility of the source. + // 'true' shows source, 'false' hides source. + // Required: Yes. + Visible bool `json:"visible"` + // The new bounds type of the source. + // Required: Yes. + BoundsType string `json:"bounds.type"` + // The new alignment of the bounding box. + // (0-2, 4-6, 8-10). + // Required: Yes. + BoundsAlignment int `json:"bounds.alignment"` + // The new width of the bounding box. + // Required: Yes. + BoundsX float64 `json:"bounds.x"` + // The new height of the bounding box. + // Required: Yes. + BoundsY float64 `json:"bounds.y"` + _request `json:",squash"` + response chan SetSceneItemPropertiesResponse +} + +// NewSetSceneItemPropertiesRequest returns a new SetSceneItemPropertiesRequest. +func NewSetSceneItemPropertiesRequest( + sceneName string, + item string, + positionX int, + positionY int, + positionAlignment int, + rotation float64, + scaleX float64, + scaleY float64, + cropTop int, + cropBottom int, + cropLeft int, + cropRight int, + visible bool, + boundsType string, + boundsAlignment int, + boundsX float64, + boundsY float64, +) SetSceneItemPropertiesRequest { + return SetSceneItemPropertiesRequest{ + sceneName, + item, + positionX, + positionY, + positionAlignment, + rotation, + scaleX, + scaleY, + cropTop, + cropBottom, + cropLeft, + cropRight, + visible, + boundsType, + boundsAlignment, + boundsX, + boundsY, + _request{ + ID_: getMessageID(), + Type_: "SetSceneItemProperties", + err: make(chan error, 1), + }, + make(chan SetSceneItemPropertiesResponse, 1), + } +} + +// Send sends the request. +func (r *SetSceneItemPropertiesRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetSceneItemPropertiesResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetSceneItemPropertiesRequest) Receive() (SetSceneItemPropertiesResponse, error) { + if !r.sent { + return SetSceneItemPropertiesResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSceneItemPropertiesResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSceneItemPropertiesResponse{}, err + case <-time.After(receiveTimeout): + return SetSceneItemPropertiesResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetSceneItemPropertiesRequest) SendReceive(c *Client) (SetSceneItemPropertiesResponse, error) { + if err := r.Send(c); err != nil { + return SetSceneItemPropertiesResponse{}, err + } + return r.Receive() +} + +// SetSceneItemPropertiesResponse : Response for SetSceneItemPropertiesRequest. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsceneitemproperties +type SetSceneItemPropertiesResponse struct { + _response `json:",squash"` +} + +// ResetSceneItemRequest : Reset a scene item. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#resetsceneitem +type ResetSceneItemRequest struct { + // Name of the scene the source belongs to. + // Defaults to the current scene. + // Required: No. + SceneName string `json:"scene-name"` + // Name of the source item. + // Required: Yes. + Item string `json:"item"` + _request `json:",squash"` + response chan ResetSceneItemResponse +} + +// NewResetSceneItemRequest returns a new ResetSceneItemRequest. +func NewResetSceneItemRequest( + sceneName string, + item string, +) ResetSceneItemRequest { + return ResetSceneItemRequest{ + sceneName, + item, + _request{ + ID_: getMessageID(), + Type_: "ResetSceneItem", + err: make(chan error, 1), + }, + make(chan ResetSceneItemResponse, 1), + } +} + +// Send sends the request. +func (r *ResetSceneItemRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp ResetSceneItemResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r ResetSceneItemRequest) Receive() (ResetSceneItemResponse, error) { + if !r.sent { + return ResetSceneItemResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ResetSceneItemResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ResetSceneItemResponse{}, err + case <-time.After(receiveTimeout): + return ResetSceneItemResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r ResetSceneItemRequest) SendReceive(c *Client) (ResetSceneItemResponse, error) { + if err := r.Send(c); err != nil { + return ResetSceneItemResponse{}, err + } + return r.Receive() +} + +// ResetSceneItemResponse : Response for ResetSceneItemRequest. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#resetsceneitem +type ResetSceneItemResponse struct { + _response `json:",squash"` +} + +// SetSceneItemRenderRequest : Show or hide a specified source item in a specified scene. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsceneitemrender +type SetSceneItemRenderRequest struct { + // Scene item name in the specified scene. + // Required: Yes. + Source string `json:"source"` + // true = shown ; false = hidden. + // Required: Yes. + Render bool `json:"render"` + // Name of the scene where the source resides. + // Defaults to the currently active scene. + // Required: No. + SceneName string `json:"scene-name"` + _request `json:",squash"` + response chan SetSceneItemRenderResponse +} + +// NewSetSceneItemRenderRequest returns a new SetSceneItemRenderRequest. +func NewSetSceneItemRenderRequest( + source string, + render bool, + sceneName string, +) SetSceneItemRenderRequest { + return SetSceneItemRenderRequest{ + source, + render, + sceneName, + _request{ + ID_: getMessageID(), + Type_: "SetSceneItemRender", + err: make(chan error, 1), + }, + make(chan SetSceneItemRenderResponse, 1), + } +} + +// Send sends the request. +func (r *SetSceneItemRenderRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetSceneItemRenderResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetSceneItemRenderRequest) Receive() (SetSceneItemRenderResponse, error) { + if !r.sent { + return SetSceneItemRenderResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSceneItemRenderResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSceneItemRenderResponse{}, err + case <-time.After(receiveTimeout): + return SetSceneItemRenderResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetSceneItemRenderRequest) SendReceive(c *Client) (SetSceneItemRenderResponse, error) { + if err := r.Send(c); err != nil { + return SetSceneItemRenderResponse{}, err + } + return r.Receive() +} + +// SetSceneItemRenderResponse : Response for SetSceneItemRenderRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsceneitemrender +type SetSceneItemRenderResponse struct { + _response `json:",squash"` +} + +// SetSceneItemPositionRequest : Sets the coordinates of a specified source item. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsceneitemposition +type SetSceneItemPositionRequest struct { + // The name of the scene that the source item belongs to. + // Defaults to the current scene. + // Required: No. + SceneName string `json:"scene-name"` + // The name of the source item. + // Required: Yes. + Item string `json:"item"` + // X coordinate. + // Required: Yes. + X float64 `json:"x"` + // Y coordinate. + // Required: Yes. + Y float64 `json:"y"` + _request `json:",squash"` + response chan SetSceneItemPositionResponse +} + +// NewSetSceneItemPositionRequest returns a new SetSceneItemPositionRequest. +func NewSetSceneItemPositionRequest( + sceneName string, + item string, + x float64, + y float64, +) SetSceneItemPositionRequest { + return SetSceneItemPositionRequest{ + sceneName, + item, + x, + y, + _request{ + ID_: getMessageID(), + Type_: "SetSceneItemPosition", + err: make(chan error, 1), + }, + make(chan SetSceneItemPositionResponse, 1), + } +} + +// Send sends the request. +func (r *SetSceneItemPositionRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetSceneItemPositionResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetSceneItemPositionRequest) Receive() (SetSceneItemPositionResponse, error) { + if !r.sent { + return SetSceneItemPositionResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSceneItemPositionResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSceneItemPositionResponse{}, err + case <-time.After(receiveTimeout): + return SetSceneItemPositionResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetSceneItemPositionRequest) SendReceive(c *Client) (SetSceneItemPositionResponse, error) { + if err := r.Send(c); err != nil { + return SetSceneItemPositionResponse{}, err + } + return r.Receive() +} + +// SetSceneItemPositionResponse : Response for SetSceneItemPositionRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsceneitemposition +type SetSceneItemPositionResponse struct { + _response `json:",squash"` +} + +// SetSceneItemTransformRequest : Set the transform of the specified source item. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsceneitemtransform +type SetSceneItemTransformRequest struct { + // The name of the scene that the source item belongs to. + // Defaults to the current scene. + // Required: No. + SceneName string `json:"scene-name"` + // The name of the source item. + // Required: Yes. + Item string `json:"item"` + // Width scale factor. + // Required: Yes. + XScale float64 `json:"x-scale"` + // Height scale factor. + // Required: Yes. + YScale float64 `json:"y-scale"` + // Source item rotation (in degrees). + // Required: Yes. + Rotation float64 `json:"rotation"` + _request `json:",squash"` + response chan SetSceneItemTransformResponse +} + +// NewSetSceneItemTransformRequest returns a new SetSceneItemTransformRequest. +func NewSetSceneItemTransformRequest( + sceneName string, + item string, + xScale float64, + yScale float64, + rotation float64, +) SetSceneItemTransformRequest { + return SetSceneItemTransformRequest{ + sceneName, + item, + xScale, + yScale, + rotation, + _request{ + ID_: getMessageID(), + Type_: "SetSceneItemTransform", + err: make(chan error, 1), + }, + make(chan SetSceneItemTransformResponse, 1), + } +} + +// Send sends the request. +func (r *SetSceneItemTransformRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetSceneItemTransformResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetSceneItemTransformRequest) Receive() (SetSceneItemTransformResponse, error) { + if !r.sent { + return SetSceneItemTransformResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSceneItemTransformResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSceneItemTransformResponse{}, err + case <-time.After(receiveTimeout): + return SetSceneItemTransformResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetSceneItemTransformRequest) SendReceive(c *Client) (SetSceneItemTransformResponse, error) { + if err := r.Send(c); err != nil { + return SetSceneItemTransformResponse{}, err + } + return r.Receive() +} + +// SetSceneItemTransformResponse : Response for SetSceneItemTransformRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsceneitemtransform +type SetSceneItemTransformResponse struct { + _response `json:",squash"` +} + +// SetSceneItemCropRequest : Sets the crop coordinates of the specified source item. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsceneitemcrop +type SetSceneItemCropRequest struct { + // the name of the scene that the source item belongs to. + // Defaults to the current scene. + // Required: No. + SceneName string `json:"scene-name"` + // The name of the source. + // Required: Yes. + Item string `json:"item"` + // Pixel position of the top of the source item. + // Required: Yes. + Top int `json:"top"` + // Pixel position of the bottom of the source item. + // Required: Yes. + Bottom int `json:"bottom"` + // Pixel position of the left of the source item. + // Required: Yes. + Left int `json:"left"` + // Pixel position of the right of the source item. + // Required: Yes. + Right int `json:"right"` + _request `json:",squash"` + response chan SetSceneItemCropResponse +} + +// NewSetSceneItemCropRequest returns a new SetSceneItemCropRequest. +func NewSetSceneItemCropRequest( + sceneName string, + item string, + top int, + bottom int, + left int, + right int, +) SetSceneItemCropRequest { + return SetSceneItemCropRequest{ + sceneName, + item, + top, + bottom, + left, + right, + _request{ + ID_: getMessageID(), + Type_: "SetSceneItemCrop", + err: make(chan error, 1), + }, + make(chan SetSceneItemCropResponse, 1), + } +} + +// Send sends the request. +func (r *SetSceneItemCropRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetSceneItemCropResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetSceneItemCropRequest) Receive() (SetSceneItemCropResponse, error) { + if !r.sent { + return SetSceneItemCropResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSceneItemCropResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSceneItemCropResponse{}, err + case <-time.After(receiveTimeout): + return SetSceneItemCropResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetSceneItemCropRequest) SendReceive(c *Client) (SetSceneItemCropResponse, error) { + if err := r.Send(c); err != nil { + return SetSceneItemCropResponse{}, err + } + return r.Receive() +} + +// SetSceneItemCropResponse : Response for SetSceneItemCropRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsceneitemcrop +type SetSceneItemCropResponse struct { + _response `json:",squash"` +} diff --git a/obsws/requests_scenes.go b/obsws/requests_scenes.go new file mode 100644 index 0000000..cbb7488 --- /dev/null +++ b/obsws/requests_scenes.go @@ -0,0 +1,286 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// SetCurrentSceneRequest : Switch to the specified scene. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setcurrentscene +type SetCurrentSceneRequest struct { + // Name of the scene to switch to. + // Required: Yes. + SceneName string `json:"scene-name"` + _request `json:",squash"` + response chan SetCurrentSceneResponse +} + +// NewSetCurrentSceneRequest returns a new SetCurrentSceneRequest. +func NewSetCurrentSceneRequest(sceneName string) SetCurrentSceneRequest { + return SetCurrentSceneRequest{ + sceneName, + _request{ + ID_: getMessageID(), + Type_: "SetCurrentScene", + err: make(chan error, 1), + }, + make(chan SetCurrentSceneResponse, 1), + } +} + +// Send sends the request. +func (r *SetCurrentSceneRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetCurrentSceneResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetCurrentSceneRequest) Receive() (SetCurrentSceneResponse, error) { + if !r.sent { + return SetCurrentSceneResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetCurrentSceneResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetCurrentSceneResponse{}, err + case <-time.After(receiveTimeout): + return SetCurrentSceneResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetCurrentSceneRequest) SendReceive(c *Client) (SetCurrentSceneResponse, error) { + if err := r.Send(c); err != nil { + return SetCurrentSceneResponse{}, err + } + return r.Receive() +} + +// SetCurrentSceneResponse : Response for SetCurrentSceneRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setcurrentscene +type SetCurrentSceneResponse struct { + _response `json:",squash"` +} + +// GetCurrentSceneRequest : Get the current scene's name and source items. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getcurrentscene +type GetCurrentSceneRequest struct { + _request `json:",squash"` + response chan GetCurrentSceneResponse +} + +// NewGetCurrentSceneRequest returns a new GetCurrentSceneRequest. +func NewGetCurrentSceneRequest() GetCurrentSceneRequest { + return GetCurrentSceneRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetCurrentScene", + err: make(chan error, 1), + }, + make(chan GetCurrentSceneResponse, 1), + } +} + +// Send sends the request. +func (r *GetCurrentSceneRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetCurrentSceneResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetCurrentSceneRequest) Receive() (GetCurrentSceneResponse, error) { + if !r.sent { + return GetCurrentSceneResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetCurrentSceneResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetCurrentSceneResponse{}, err + case <-time.After(receiveTimeout): + return GetCurrentSceneResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetCurrentSceneRequest) SendReceive(c *Client) (GetCurrentSceneResponse, error) { + if err := r.Send(c); err != nil { + return GetCurrentSceneResponse{}, err + } + return r.Receive() +} + +// GetCurrentSceneResponse : Response for GetCurrentSceneRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getcurrentscene +type GetCurrentSceneResponse struct { + // Name of the currently active scene. + // Required: Yes. + Name string `json:"name"` + // Ordered list of the current scene's source items. + // Required: Yes. + Sources []map[string]interface{} `json:"sources"` + _response `json:",squash"` +} + +// GetSceneListRequest : Get a list of scenes in the currently active profile. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getscenelist +type GetSceneListRequest struct { + _request `json:",squash"` + response chan GetSceneListResponse +} + +// NewGetSceneListRequest returns a new GetSceneListRequest. +func NewGetSceneListRequest() GetSceneListRequest { + return GetSceneListRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetSceneList", + err: make(chan error, 1), + }, + make(chan GetSceneListResponse, 1), + } +} + +// Send sends the request. +func (r *GetSceneListRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetSceneListResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetSceneListRequest) Receive() (GetSceneListResponse, error) { + if !r.sent { + return GetSceneListResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSceneListResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSceneListResponse{}, err + case <-time.After(receiveTimeout): + return GetSceneListResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetSceneListRequest) SendReceive(c *Client) (GetSceneListResponse, error) { + if err := r.Send(c); err != nil { + return GetSceneListResponse{}, err + } + return r.Receive() +} + +// GetSceneListResponse : Response for GetSceneListRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getscenelist +type GetSceneListResponse struct { + // Name of the currently active scene. + // Required: Yes. + CurrentScene string `json:"current-scene"` + // Ordered list of the current profile's scenes (See `[GetCurrentScene](#getcurrentscene)` for more information). + // Required: Yes. + Scenes []map[string]interface{} `json:"scenes"` + _response `json:",squash"` +} diff --git a/obsws/requests_sources.go b/obsws/requests_sources.go new file mode 100644 index 0000000..a272deb --- /dev/null +++ b/obsws/requests_sources.go @@ -0,0 +1,2784 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// GetSourcesListRequest : List all sources available in the running OBS instance. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsourceslist +type GetSourcesListRequest struct { + _request `json:",squash"` + response chan GetSourcesListResponse +} + +// NewGetSourcesListRequest returns a new GetSourcesListRequest. +func NewGetSourcesListRequest() GetSourcesListRequest { + return GetSourcesListRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetSourcesList", + err: make(chan error, 1), + }, + make(chan GetSourcesListResponse, 1), + } +} + +// Send sends the request. +func (r *GetSourcesListRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetSourcesListResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetSourcesListRequest) Receive() (GetSourcesListResponse, error) { + if !r.sent { + return GetSourcesListResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSourcesListResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSourcesListResponse{}, err + case <-time.After(receiveTimeout): + return GetSourcesListResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetSourcesListRequest) SendReceive(c *Client) (GetSourcesListResponse, error) { + if err := r.Send(c); err != nil { + return GetSourcesListResponse{}, err + } + return r.Receive() +} + +// GetSourcesListResponse : Response for GetSourcesListRequest. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsourceslist +type GetSourcesListResponse struct { + // Array of sources as objects. + // Required: Yes. + Sources []map[string]interface{} `json:"sources"` + // Unique source name. + // Required: Yes. + SourcesName string `json:"sources.*.name"` + // Non-unique source internal type (a.k.a type id). + // Required: Yes. + SourcesTypeID string `json:"sources.*.typeId"` + // Source type. + // Value is one of the following: "input", "filter", "transition", "scene" or "unknown". + // Required: Yes. + SourcesType string `json:"sources.*.type"` + _response `json:",squash"` +} + +// GetSourcesTypesListRequest : Get a list of all available sources types. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsourcestypeslist +type GetSourcesTypesListRequest struct { + _request `json:",squash"` + response chan GetSourcesTypesListResponse +} + +// NewGetSourcesTypesListRequest returns a new GetSourcesTypesListRequest. +func NewGetSourcesTypesListRequest() GetSourcesTypesListRequest { + return GetSourcesTypesListRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetSourcesTypesList", + err: make(chan error, 1), + }, + make(chan GetSourcesTypesListResponse, 1), + } +} + +// Send sends the request. +func (r *GetSourcesTypesListRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetSourcesTypesListResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetSourcesTypesListRequest) Receive() (GetSourcesTypesListResponse, error) { + if !r.sent { + return GetSourcesTypesListResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSourcesTypesListResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSourcesTypesListResponse{}, err + case <-time.After(receiveTimeout): + return GetSourcesTypesListResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetSourcesTypesListRequest) SendReceive(c *Client) (GetSourcesTypesListResponse, error) { + if err := r.Send(c); err != nil { + return GetSourcesTypesListResponse{}, err + } + return r.Receive() +} + +// GetSourcesTypesListResponse : Response for GetSourcesTypesListRequest. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsourcestypeslist +type GetSourcesTypesListResponse struct { + // Array of sources as objects. + // Required: Yes. + IDs []map[string]interface{} `json:"ids"` + // Non-unique internal source type ID. + // Required: Yes. + IDsTypeID string `json:"ids.*.typeId"` + // Display name of the source type. + // Required: Yes. + IDsDisplayName string `json:"ids.*.displayName"` + // Type. + // Value is one of the following: "input", "filter", "transition" or "other". + // Required: Yes. + IDsType string `json:"ids.*.type"` + // Default settings of this source type. + // Required: Yes. + IDsDefaultSettings map[string]interface{} `json:"ids.*.defaultSettings"` + // Source type capabilities. + // Required: Yes. + IDsCaps map[string]interface{} `json:"ids.*.caps"` + // True if source of this type provide frames asynchronously. + // Required: Yes. + IDsCapsIsAsync bool `json:"ids.*.caps.isAsync"` + // True if sources of this type provide video. + // Required: Yes. + IDsCapsHasVideo bool `json:"ids.*.caps.hasVideo"` + // True if sources of this type provide audio. + // Required: Yes. + IDsCapsHasAudio bool `json:"ids.*.caps.hasAudio"` + // True if interaction with this sources of this type is possible. + // Required: Yes. + IDsCapsCanInteract bool `json:"ids.*.caps.canInteract"` + // True if sources of this type composite one or more sub-sources. + // Required: Yes. + IDsCapsIsComposite bool `json:"ids.*.caps.isComposite"` + // True if sources of this type should not be fully duplicated. + // Required: Yes. + IDsCapsDoNotDuplicate bool `json:"ids.*.caps.doNotDuplicate"` + // True if sources of this type may cause a feedback loop if it's audio is monitored and shouldn't be. + // Required: Yes. + IDsCapsDoNotSelfMonitor bool `json:"ids.*.caps.doNotSelfMonitor"` + _response `json:",squash"` +} + +// GetVolumeRequest : Get the volume of the specified source. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getvolume +type GetVolumeRequest struct { + // Name of the source. + // Required: Yes. + Source string `json:"source"` + _request `json:",squash"` + response chan GetVolumeResponse +} + +// NewGetVolumeRequest returns a new GetVolumeRequest. +func NewGetVolumeRequest(source string) GetVolumeRequest { + return GetVolumeRequest{ + source, + _request{ + ID_: getMessageID(), + Type_: "GetVolume", + err: make(chan error, 1), + }, + make(chan GetVolumeResponse, 1), + } +} + +// Send sends the request. +func (r *GetVolumeRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetVolumeResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetVolumeRequest) Receive() (GetVolumeResponse, error) { + if !r.sent { + return GetVolumeResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetVolumeResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetVolumeResponse{}, err + case <-time.After(receiveTimeout): + return GetVolumeResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetVolumeRequest) SendReceive(c *Client) (GetVolumeResponse, error) { + if err := r.Send(c); err != nil { + return GetVolumeResponse{}, err + } + return r.Receive() +} + +// GetVolumeResponse : Response for GetVolumeRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getvolume +type GetVolumeResponse struct { + // Name of the source. + // Required: Yes. + Name string `json:"name"` + // Volume of the source. + // Between `0.0` and `1.0`. + // Required: Yes. + Volume float64 `json:"volume"` + // Indicates whether the source is muted. + // Required: Yes. + Mute bool `json:"mute"` + _response `json:",squash"` +} + +// SetVolumeRequest : Set the volume of the specified source. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setvolume +type SetVolumeRequest struct { + // Name of the source. + // Required: Yes. + Source string `json:"source"` + // Desired volume. + // Must be between `0.0` and `1.0`. + // Required: Yes. + Volume float64 `json:"volume"` + _request `json:",squash"` + response chan SetVolumeResponse +} + +// NewSetVolumeRequest returns a new SetVolumeRequest. +func NewSetVolumeRequest( + source string, + volume float64, +) SetVolumeRequest { + return SetVolumeRequest{ + source, + volume, + _request{ + ID_: getMessageID(), + Type_: "SetVolume", + err: make(chan error, 1), + }, + make(chan SetVolumeResponse, 1), + } +} + +// Send sends the request. +func (r *SetVolumeRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetVolumeResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetVolumeRequest) Receive() (SetVolumeResponse, error) { + if !r.sent { + return SetVolumeResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetVolumeResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetVolumeResponse{}, err + case <-time.After(receiveTimeout): + return SetVolumeResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetVolumeRequest) SendReceive(c *Client) (SetVolumeResponse, error) { + if err := r.Send(c); err != nil { + return SetVolumeResponse{}, err + } + return r.Receive() +} + +// SetVolumeResponse : Response for SetVolumeRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setvolume +type SetVolumeResponse struct { + _response `json:",squash"` +} + +// GetMuteRequest : Get the mute status of a specified source. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getmute +type GetMuteRequest struct { + // The name of the source. + // Required: Yes. + Source string `json:"source"` + _request `json:",squash"` + response chan GetMuteResponse +} + +// NewGetMuteRequest returns a new GetMuteRequest. +func NewGetMuteRequest(source string) GetMuteRequest { + return GetMuteRequest{ + source, + _request{ + ID_: getMessageID(), + Type_: "GetMute", + err: make(chan error, 1), + }, + make(chan GetMuteResponse, 1), + } +} + +// Send sends the request. +func (r *GetMuteRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetMuteResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetMuteRequest) Receive() (GetMuteResponse, error) { + if !r.sent { + return GetMuteResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetMuteResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetMuteResponse{}, err + case <-time.After(receiveTimeout): + return GetMuteResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetMuteRequest) SendReceive(c *Client) (GetMuteResponse, error) { + if err := r.Send(c); err != nil { + return GetMuteResponse{}, err + } + return r.Receive() +} + +// GetMuteResponse : Response for GetMuteRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getmute +type GetMuteResponse struct { + // The name of the source. + // Required: Yes. + Name string `json:"name"` + // Mute status of the source. + // Required: Yes. + Muted bool `json:"muted"` + _response `json:",squash"` +} + +// SetMuteRequest : Sets the mute status of a specified source. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setmute +type SetMuteRequest struct { + // The name of the source. + // Required: Yes. + Source string `json:"source"` + // Desired mute status. + // Required: Yes. + Mute bool `json:"mute"` + _request `json:",squash"` + response chan SetMuteResponse +} + +// NewSetMuteRequest returns a new SetMuteRequest. +func NewSetMuteRequest( + source string, + mute bool, +) SetMuteRequest { + return SetMuteRequest{ + source, + mute, + _request{ + ID_: getMessageID(), + Type_: "SetMute", + err: make(chan error, 1), + }, + make(chan SetMuteResponse, 1), + } +} + +// Send sends the request. +func (r *SetMuteRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetMuteResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetMuteRequest) Receive() (SetMuteResponse, error) { + if !r.sent { + return SetMuteResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetMuteResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetMuteResponse{}, err + case <-time.After(receiveTimeout): + return SetMuteResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetMuteRequest) SendReceive(c *Client) (SetMuteResponse, error) { + if err := r.Send(c); err != nil { + return SetMuteResponse{}, err + } + return r.Receive() +} + +// SetMuteResponse : Response for SetMuteRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setmute +type SetMuteResponse struct { + _response `json:",squash"` +} + +// ToggleMuteRequest : Inverts the mute status of a specified source. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#togglemute +type ToggleMuteRequest struct { + // The name of the source. + // Required: Yes. + Source string `json:"source"` + _request `json:",squash"` + response chan ToggleMuteResponse +} + +// NewToggleMuteRequest returns a new ToggleMuteRequest. +func NewToggleMuteRequest(source string) ToggleMuteRequest { + return ToggleMuteRequest{ + source, + _request{ + ID_: getMessageID(), + Type_: "ToggleMute", + err: make(chan error, 1), + }, + make(chan ToggleMuteResponse, 1), + } +} + +// Send sends the request. +func (r *ToggleMuteRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp ToggleMuteResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r ToggleMuteRequest) Receive() (ToggleMuteResponse, error) { + if !r.sent { + return ToggleMuteResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ToggleMuteResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ToggleMuteResponse{}, err + case <-time.After(receiveTimeout): + return ToggleMuteResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r ToggleMuteRequest) SendReceive(c *Client) (ToggleMuteResponse, error) { + if err := r.Send(c); err != nil { + return ToggleMuteResponse{}, err + } + return r.Receive() +} + +// ToggleMuteResponse : Response for ToggleMuteRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#togglemute +type ToggleMuteResponse struct { + _response `json:",squash"` +} + +// SetSyncOffsetRequest : Set the audio sync offset of a specified source. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsyncoffset +type SetSyncOffsetRequest struct { + // The name of the source. + // Required: Yes. + Source string `json:"source"` + // The desired audio sync offset (in nanoseconds). + // Required: Yes. + Offset int `json:"offset"` + _request `json:",squash"` + response chan SetSyncOffsetResponse +} + +// NewSetSyncOffsetRequest returns a new SetSyncOffsetRequest. +func NewSetSyncOffsetRequest( + source string, + offset int, +) SetSyncOffsetRequest { + return SetSyncOffsetRequest{ + source, + offset, + _request{ + ID_: getMessageID(), + Type_: "SetSyncOffset", + err: make(chan error, 1), + }, + make(chan SetSyncOffsetResponse, 1), + } +} + +// Send sends the request. +func (r *SetSyncOffsetRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetSyncOffsetResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetSyncOffsetRequest) Receive() (SetSyncOffsetResponse, error) { + if !r.sent { + return SetSyncOffsetResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSyncOffsetResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSyncOffsetResponse{}, err + case <-time.After(receiveTimeout): + return SetSyncOffsetResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetSyncOffsetRequest) SendReceive(c *Client) (SetSyncOffsetResponse, error) { + if err := r.Send(c); err != nil { + return SetSyncOffsetResponse{}, err + } + return r.Receive() +} + +// SetSyncOffsetResponse : Response for SetSyncOffsetRequest. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsyncoffset +type SetSyncOffsetResponse struct { + _response `json:",squash"` +} + +// GetSyncOffsetRequest : Get the audio sync offset of a specified source. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsyncoffset +type GetSyncOffsetRequest struct { + // The name of the source. + // Required: Yes. + Source string `json:"source"` + _request `json:",squash"` + response chan GetSyncOffsetResponse +} + +// NewGetSyncOffsetRequest returns a new GetSyncOffsetRequest. +func NewGetSyncOffsetRequest(source string) GetSyncOffsetRequest { + return GetSyncOffsetRequest{ + source, + _request{ + ID_: getMessageID(), + Type_: "GetSyncOffset", + err: make(chan error, 1), + }, + make(chan GetSyncOffsetResponse, 1), + } +} + +// Send sends the request. +func (r *GetSyncOffsetRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetSyncOffsetResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetSyncOffsetRequest) Receive() (GetSyncOffsetResponse, error) { + if !r.sent { + return GetSyncOffsetResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSyncOffsetResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSyncOffsetResponse{}, err + case <-time.After(receiveTimeout): + return GetSyncOffsetResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetSyncOffsetRequest) SendReceive(c *Client) (GetSyncOffsetResponse, error) { + if err := r.Send(c); err != nil { + return GetSyncOffsetResponse{}, err + } + return r.Receive() +} + +// GetSyncOffsetResponse : Response for GetSyncOffsetRequest. +// +// Since obs-websocket version: 4.2.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsyncoffset +type GetSyncOffsetResponse struct { + // The name of the source. + // Required: Yes. + Name string `json:"name"` + // The audio sync offset (in nanoseconds). + // Required: Yes. + Offset int `json:"offset"` + _response `json:",squash"` +} + +// GetSourceSettingsRequest : Get settings of the specified source. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsourcesettings +type GetSourceSettingsRequest struct { + // Name of the source item. + // Required: Yes. + SourceName string `json:"sourceName"` + // Type of the specified source. + // Useful for type-checking if you expect a specific settings schema. + // Required: No. + SourceType string `json:"sourceType"` + _request `json:",squash"` + response chan GetSourceSettingsResponse +} + +// NewGetSourceSettingsRequest returns a new GetSourceSettingsRequest. +func NewGetSourceSettingsRequest( + sourceName string, + sourceType string, +) GetSourceSettingsRequest { + return GetSourceSettingsRequest{ + sourceName, + sourceType, + _request{ + ID_: getMessageID(), + Type_: "GetSourceSettings", + err: make(chan error, 1), + }, + make(chan GetSourceSettingsResponse, 1), + } +} + +// Send sends the request. +func (r *GetSourceSettingsRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetSourceSettingsResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetSourceSettingsRequest) Receive() (GetSourceSettingsResponse, error) { + if !r.sent { + return GetSourceSettingsResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSourceSettingsResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSourceSettingsResponse{}, err + case <-time.After(receiveTimeout): + return GetSourceSettingsResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetSourceSettingsRequest) SendReceive(c *Client) (GetSourceSettingsResponse, error) { + if err := r.Send(c); err != nil { + return GetSourceSettingsResponse{}, err + } + return r.Receive() +} + +// GetSourceSettingsResponse : Response for GetSourceSettingsRequest. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsourcesettings +type GetSourceSettingsResponse struct { + // Source name. + // Required: Yes. + SourceName string `json:"sourceName"` + // Type of the specified source. + // Required: Yes. + SourceType string `json:"sourceType"` + // Source settings. + // Varying between source types. + // Required: Yes. + SourceSettings map[string]interface{} `json:"sourceSettings"` + _response `json:",squash"` +} + +// SetSourceSettingsRequest : Set settings of the specified source. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsourcesettings +type SetSourceSettingsRequest struct { + // Name of the source item. + // Required: Yes. + SourceName string `json:"sourceName"` + // Type of the specified source. + // Useful for type-checking to avoid settings a set of settings incompatible with the actual source's type. + // Required: No. + SourceType string `json:"sourceType"` + // Source settings. + // Varying between source types. + // Required: Yes. + SourceSettings map[string]interface{} `json:"sourceSettings"` + _request `json:",squash"` + response chan SetSourceSettingsResponse +} + +// NewSetSourceSettingsRequest returns a new SetSourceSettingsRequest. +func NewSetSourceSettingsRequest( + sourceName string, + sourceType string, + sourceSettings map[string]interface{}, +) SetSourceSettingsRequest { + return SetSourceSettingsRequest{ + sourceName, + sourceType, + sourceSettings, + _request{ + ID_: getMessageID(), + Type_: "SetSourceSettings", + err: make(chan error, 1), + }, + make(chan SetSourceSettingsResponse, 1), + } +} + +// Send sends the request. +func (r *SetSourceSettingsRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetSourceSettingsResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetSourceSettingsRequest) Receive() (SetSourceSettingsResponse, error) { + if !r.sent { + return SetSourceSettingsResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSourceSettingsResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSourceSettingsResponse{}, err + case <-time.After(receiveTimeout): + return SetSourceSettingsResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetSourceSettingsRequest) SendReceive(c *Client) (SetSourceSettingsResponse, error) { + if err := r.Send(c); err != nil { + return SetSourceSettingsResponse{}, err + } + return r.Receive() +} + +// SetSourceSettingsResponse : Response for SetSourceSettingsRequest. +// +// Since obs-websocket version: 4.3.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsourcesettings +type SetSourceSettingsResponse struct { + // Source name. + // Required: Yes. + SourceName string `json:"sourceName"` + // Type of the specified source. + // Required: Yes. + SourceType string `json:"sourceType"` + // Source settings. + // Varying between source types. + // Required: Yes. + SourceSettings map[string]interface{} `json:"sourceSettings"` + _response `json:",squash"` +} + +// GetTextGDIPlusPropertiesRequest : Get the current properties of a Text GDI Plus source. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#gettextgdiplusproperties +type GetTextGDIPlusPropertiesRequest struct { + // Name of the scene to retrieve. + // Defaults to the current scene. + // Required: No. + SceneName string `json:"scene-name"` + // Name of the source. + // Required: Yes. + Source string `json:"source"` + _request `json:",squash"` + response chan GetTextGDIPlusPropertiesResponse +} + +// NewGetTextGDIPlusPropertiesRequest returns a new GetTextGDIPlusPropertiesRequest. +func NewGetTextGDIPlusPropertiesRequest( + sceneName string, + source string, +) GetTextGDIPlusPropertiesRequest { + return GetTextGDIPlusPropertiesRequest{ + sceneName, + source, + _request{ + ID_: getMessageID(), + Type_: "GetTextGDIPlusProperties", + err: make(chan error, 1), + }, + make(chan GetTextGDIPlusPropertiesResponse, 1), + } +} + +// Send sends the request. +func (r *GetTextGDIPlusPropertiesRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetTextGDIPlusPropertiesResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetTextGDIPlusPropertiesRequest) Receive() (GetTextGDIPlusPropertiesResponse, error) { + if !r.sent { + return GetTextGDIPlusPropertiesResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetTextGDIPlusPropertiesResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetTextGDIPlusPropertiesResponse{}, err + case <-time.After(receiveTimeout): + return GetTextGDIPlusPropertiesResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetTextGDIPlusPropertiesRequest) SendReceive(c *Client) (GetTextGDIPlusPropertiesResponse, error) { + if err := r.Send(c); err != nil { + return GetTextGDIPlusPropertiesResponse{}, err + } + return r.Receive() +} + +// GetTextGDIPlusPropertiesResponse : Response for GetTextGDIPlusPropertiesRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#gettextgdiplusproperties +type GetTextGDIPlusPropertiesResponse struct { + // Text Alignment ("left", "center", "right"). + // Required: Yes. + Align string `json:"align"` + // Background color. + // Required: Yes. + BkColor int `json:"bk-color"` + // Background opacity (0-100). + // Required: Yes. + BkOpacity int `json:"bk-opacity"` + // Chat log. + // Required: Yes. + Chatlog bool `json:"chatlog"` + // Chat log lines. + // Required: Yes. + ChatlogLines int `json:"chatlog_lines"` + // Text color. + // Required: Yes. + Color int `json:"color"` + // Extents wrap. + // Required: Yes. + Extents bool `json:"extents"` + // Extents cx. + // Required: Yes. + ExtentsCx int `json:"extents_cx"` + // Extents cy. + // Required: Yes. + ExtentsCy int `json:"extents_cy"` + // File path name. + // Required: Yes. + File string `json:"file"` + // Read text from the specified file. + // Required: Yes. + ReadFromFile bool `json:"read_from_file"` + // Holds data for the font. + // Ex: `"font": { "face": "Arial", "flags": 0, "size": 150, "style": "" }`. + // Required: Yes. + Font map[string]interface{} `json:"font"` + // Font face. + // Required: Yes. + FontFace string `json:"font.face"` + // Font text styling flag. + // `Bold=1, Italic=2, Bold Italic=3, Underline=5, Strikeout=8`. + // Required: Yes. + FontFlags int `json:"font.flags"` + // Font text size. + // Required: Yes. + FontSize int `json:"font.size"` + // Font Style (unknown function). + // Required: Yes. + FontStyle string `json:"font.style"` + // Gradient enabled. + // Required: Yes. + Gradient bool `json:"gradient"` + // Gradient color. + // Required: Yes. + GradientColor int `json:"gradient_color"` + // Gradient direction. + // Required: Yes. + GradientDir float64 `json:"gradient_dir"` + // Gradient opacity (0-100). + // Required: Yes. + GradientOpacity int `json:"gradient_opacity"` + // Outline. + // Required: Yes. + Outline bool `json:"outline"` + // Outline color. + // Required: Yes. + OutlineColor int `json:"outline_color"` + // Outline size. + // Required: Yes. + OutlineSize int `json:"outline_size"` + // Outline opacity (0-100). + // Required: Yes. + OutlineOpacity int `json:"outline_opacity"` + // Text content to be displayed. + // Required: Yes. + Text string `json:"text"` + // Text vertical alignment ("top", "center", "bottom"). + // Required: Yes. + Valign string `json:"valign"` + // Vertical text enabled. + // Required: Yes. + Vertical bool `json:"vertical"` + // Visibility of the scene item. + // Required: Yes. + Render bool `json:"render"` + _response `json:",squash"` +} + +// SetTextGDIPlusPropertiesRequest : Get the current properties of a Text GDI Plus source. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#settextgdiplusproperties +type SetTextGDIPlusPropertiesRequest struct { + // Name of the scene to retrieve. + // Defaults to the current scene. + // Required: No. + SceneName string `json:"scene-name"` + // Name of the source. + // Required: Yes. + Source string `json:"source"` + // Text Alignment ("left", "center", "right"). + // Required: No. + Align string `json:"align"` + // Background color. + // Required: No. + BkColor int `json:"bk-color"` + // Background opacity (0-100). + // Required: No. + BkOpacity int `json:"bk-opacity"` + // Chat log. + // Required: No. + Chatlog bool `json:"chatlog"` + // Chat log lines. + // Required: No. + ChatlogLines int `json:"chatlog_lines"` + // Text color. + // Required: No. + Color int `json:"color"` + // Extents wrap. + // Required: No. + Extents bool `json:"extents"` + // Extents cx. + // Required: No. + ExtentsCx int `json:"extents_cx"` + // Extents cy. + // Required: No. + ExtentsCy int `json:"extents_cy"` + // File path name. + // Required: No. + File string `json:"file"` + // Read text from the specified file. + // Required: No. + ReadFromFile bool `json:"read_from_file"` + // Holds data for the font. + // Ex: `"font": { "face": "Arial", "flags": 0, "size": 150, "style": "" }`. + // Required: No. + Font map[string]interface{} `json:"font"` + // Font face. + // Required: No. + FontFace string `json:"font.face"` + // Font text styling flag. + // `Bold=1, Italic=2, Bold Italic=3, Underline=5, Strikeout=8`. + // Required: No. + FontFlags int `json:"font.flags"` + // Font text size. + // Required: No. + FontSize int `json:"font.size"` + // Font Style (unknown function). + // Required: No. + FontStyle string `json:"font.style"` + // Gradient enabled. + // Required: No. + Gradient bool `json:"gradient"` + // Gradient color. + // Required: No. + GradientColor int `json:"gradient_color"` + // Gradient direction. + // Required: No. + GradientDir float64 `json:"gradient_dir"` + // Gradient opacity (0-100). + // Required: No. + GradientOpacity int `json:"gradient_opacity"` + // Outline. + // Required: No. + Outline bool `json:"outline"` + // Outline color. + // Required: No. + OutlineColor int `json:"outline_color"` + // Outline size. + // Required: No. + OutlineSize int `json:"outline_size"` + // Outline opacity (0-100). + // Required: No. + OutlineOpacity int `json:"outline_opacity"` + // Text content to be displayed. + // Required: No. + Text string `json:"text"` + // Text vertical alignment ("top", "center", "bottom"). + // Required: No. + Valign string `json:"valign"` + // Vertical text enabled. + // Required: No. + Vertical bool `json:"vertical"` + // Visibility of the scene item. + // Required: No. + Render bool `json:"render"` + _request `json:",squash"` + response chan SetTextGDIPlusPropertiesResponse +} + +// NewSetTextGDIPlusPropertiesRequest returns a new SetTextGDIPlusPropertiesRequest. +func NewSetTextGDIPlusPropertiesRequest( + sceneName string, + source string, + align string, + bkColor int, + bkOpacity int, + chatlog bool, + chatlogLines int, + color int, + extents bool, + extentsCx int, + extentsCy int, + file string, + readFromFile bool, + font map[string]interface{}, + fontFace string, + fontFlags int, + fontSize int, + fontStyle string, + gradient bool, + gradientColor int, + gradientDir float64, + gradientOpacity int, + outline bool, + outlineColor int, + outlineSize int, + outlineOpacity int, + text string, + valign string, + vertical bool, + render bool, +) SetTextGDIPlusPropertiesRequest { + return SetTextGDIPlusPropertiesRequest{ + sceneName, + source, + align, + bkColor, + bkOpacity, + chatlog, + chatlogLines, + color, + extents, + extentsCx, + extentsCy, + file, + readFromFile, + font, + fontFace, + fontFlags, + fontSize, + fontStyle, + gradient, + gradientColor, + gradientDir, + gradientOpacity, + outline, + outlineColor, + outlineSize, + outlineOpacity, + text, + valign, + vertical, + render, + _request{ + ID_: getMessageID(), + Type_: "SetTextGDIPlusProperties", + err: make(chan error, 1), + }, + make(chan SetTextGDIPlusPropertiesResponse, 1), + } +} + +// Send sends the request. +func (r *SetTextGDIPlusPropertiesRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetTextGDIPlusPropertiesResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetTextGDIPlusPropertiesRequest) Receive() (SetTextGDIPlusPropertiesResponse, error) { + if !r.sent { + return SetTextGDIPlusPropertiesResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetTextGDIPlusPropertiesResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetTextGDIPlusPropertiesResponse{}, err + case <-time.After(receiveTimeout): + return SetTextGDIPlusPropertiesResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetTextGDIPlusPropertiesRequest) SendReceive(c *Client) (SetTextGDIPlusPropertiesResponse, error) { + if err := r.Send(c); err != nil { + return SetTextGDIPlusPropertiesResponse{}, err + } + return r.Receive() +} + +// SetTextGDIPlusPropertiesResponse : Response for SetTextGDIPlusPropertiesRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#settextgdiplusproperties +type SetTextGDIPlusPropertiesResponse struct { + _response `json:",squash"` +} + +// GetBrowserSourcePropertiesRequest : Get current properties for a Browser Source. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getbrowsersourceproperties +type GetBrowserSourcePropertiesRequest struct { + // Name of the scene that the source belongs to. + // Defaults to the current scene. + // Required: No. + SceneName string `json:"scene-name"` + // Name of the source. + // Required: Yes. + Source string `json:"source"` + _request `json:",squash"` + response chan GetBrowserSourcePropertiesResponse +} + +// NewGetBrowserSourcePropertiesRequest returns a new GetBrowserSourcePropertiesRequest. +func NewGetBrowserSourcePropertiesRequest( + sceneName string, + source string, +) GetBrowserSourcePropertiesRequest { + return GetBrowserSourcePropertiesRequest{ + sceneName, + source, + _request{ + ID_: getMessageID(), + Type_: "GetBrowserSourceProperties", + err: make(chan error, 1), + }, + make(chan GetBrowserSourcePropertiesResponse, 1), + } +} + +// Send sends the request. +func (r *GetBrowserSourcePropertiesRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetBrowserSourcePropertiesResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetBrowserSourcePropertiesRequest) Receive() (GetBrowserSourcePropertiesResponse, error) { + if !r.sent { + return GetBrowserSourcePropertiesResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetBrowserSourcePropertiesResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetBrowserSourcePropertiesResponse{}, err + case <-time.After(receiveTimeout): + return GetBrowserSourcePropertiesResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetBrowserSourcePropertiesRequest) SendReceive(c *Client) (GetBrowserSourcePropertiesResponse, error) { + if err := r.Send(c); err != nil { + return GetBrowserSourcePropertiesResponse{}, err + } + return r.Receive() +} + +// GetBrowserSourcePropertiesResponse : Response for GetBrowserSourcePropertiesRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getbrowsersourceproperties +type GetBrowserSourcePropertiesResponse struct { + // Indicates that a local file is in use. + // Required: Yes. + IsLocalFile bool `json:"is_local_file"` + // file path. + // Required: Yes. + LocalFile string `json:"local_file"` + // Url. + // Required: Yes. + Url string `json:"url"` + // CSS to inject. + // Required: Yes. + Css string `json:"css"` + // Width. + // Required: Yes. + Width int `json:"width"` + // Height. + // Required: Yes. + Height int `json:"height"` + // Framerate. + // Required: Yes. + FPS int `json:"fps"` + // Indicates whether the source should be shutdown when not visible. + // Required: Yes. + Shutdown bool `json:"shutdown"` + // Visibility of the scene item. + // Required: No. + Render bool `json:"render"` + _response `json:",squash"` +} + +// SetBrowserSourcePropertiesRequest : Set current properties for a Browser Source. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setbrowsersourceproperties +type SetBrowserSourcePropertiesRequest struct { + // Name of the scene that the source belongs to. + // Defaults to the current scene. + // Required: No. + SceneName string `json:"scene-name"` + // Name of the source. + // Required: Yes. + Source string `json:"source"` + // Indicates that a local file is in use. + // Required: No. + IsLocalFile bool `json:"is_local_file"` + // file path. + // Required: No. + LocalFile string `json:"local_file"` + // Url. + // Required: No. + Url string `json:"url"` + // CSS to inject. + // Required: No. + Css string `json:"css"` + // Width. + // Required: No. + Width int `json:"width"` + // Height. + // Required: No. + Height int `json:"height"` + // Framerate. + // Required: No. + FPS int `json:"fps"` + // Indicates whether the source should be shutdown when not visible. + // Required: No. + Shutdown bool `json:"shutdown"` + // Visibility of the scene item. + // Required: No. + Render bool `json:"render"` + _request `json:",squash"` + response chan SetBrowserSourcePropertiesResponse +} + +// NewSetBrowserSourcePropertiesRequest returns a new SetBrowserSourcePropertiesRequest. +func NewSetBrowserSourcePropertiesRequest( + sceneName string, + source string, + isLocalFile bool, + localFile string, + url string, + css string, + width int, + height int, + fps int, + shutdown bool, + render bool, +) SetBrowserSourcePropertiesRequest { + return SetBrowserSourcePropertiesRequest{ + sceneName, + source, + isLocalFile, + localFile, + url, + css, + width, + height, + fps, + shutdown, + render, + _request{ + ID_: getMessageID(), + Type_: "SetBrowserSourceProperties", + err: make(chan error, 1), + }, + make(chan SetBrowserSourcePropertiesResponse, 1), + } +} + +// Send sends the request. +func (r *SetBrowserSourcePropertiesRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetBrowserSourcePropertiesResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetBrowserSourcePropertiesRequest) Receive() (SetBrowserSourcePropertiesResponse, error) { + if !r.sent { + return SetBrowserSourcePropertiesResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetBrowserSourcePropertiesResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetBrowserSourcePropertiesResponse{}, err + case <-time.After(receiveTimeout): + return SetBrowserSourcePropertiesResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetBrowserSourcePropertiesRequest) SendReceive(c *Client) (SetBrowserSourcePropertiesResponse, error) { + if err := r.Send(c); err != nil { + return SetBrowserSourcePropertiesResponse{}, err + } + return r.Receive() +} + +// SetBrowserSourcePropertiesResponse : Response for SetBrowserSourcePropertiesRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setbrowsersourceproperties +type SetBrowserSourcePropertiesResponse struct { + _response `json:",squash"` +} + +// DeleteSceneItemRequest : Deletes a scene item. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#deletesceneitem +type DeleteSceneItemRequest struct { + // Name of the scene the source belongs to. + // Defaults to the current scene. + // Required: No. + Scene string `json:"scene"` + // item to delete (required). + // Required: Yes. + Item map[string]interface{} `json:"item"` + // name of the scene item (prefer `id`, including both is acceptable). + // Required: Yes. + ItemName string `json:"item.name"` + // id of the scene item. + // Required: Yes. + ItemID int `json:"item.id"` + _request `json:",squash"` + response chan DeleteSceneItemResponse +} + +// NewDeleteSceneItemRequest returns a new DeleteSceneItemRequest. +func NewDeleteSceneItemRequest( + scene string, + item map[string]interface{}, + itemName string, + itemID int, +) DeleteSceneItemRequest { + return DeleteSceneItemRequest{ + scene, + item, + itemName, + itemID, + _request{ + ID_: getMessageID(), + Type_: "DeleteSceneItem", + err: make(chan error, 1), + }, + make(chan DeleteSceneItemResponse, 1), + } +} + +// Send sends the request. +func (r *DeleteSceneItemRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp DeleteSceneItemResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r DeleteSceneItemRequest) Receive() (DeleteSceneItemResponse, error) { + if !r.sent { + return DeleteSceneItemResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return DeleteSceneItemResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return DeleteSceneItemResponse{}, err + case <-time.After(receiveTimeout): + return DeleteSceneItemResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r DeleteSceneItemRequest) SendReceive(c *Client) (DeleteSceneItemResponse, error) { + if err := r.Send(c); err != nil { + return DeleteSceneItemResponse{}, err + } + return r.Receive() +} + +// DeleteSceneItemResponse : Response for DeleteSceneItemRequest. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#deletesceneitem +type DeleteSceneItemResponse struct { + _response `json:",squash"` +} + +// DuplicateSceneItemRequest : Duplicates a scene item. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#duplicatesceneitem +type DuplicateSceneItemRequest struct { + // Name of the scene to copy the item from. + // Defaults to the current scene. + // Required: No. + FromScene string `json:"fromScene"` + // Name of the scene to create the item in. + // Defaults to the current scene. + // Required: No. + ToScene string `json:"toScene"` + // item to delete (required). + // Required: Yes. + Item map[string]interface{} `json:"item"` + // name of the scene item (prefer `id`, including both is acceptable). + // Required: Yes. + ItemName string `json:"item.name"` + // id of the scene item. + // Required: Yes. + ItemID int `json:"item.id"` + _request `json:",squash"` + response chan DuplicateSceneItemResponse +} + +// NewDuplicateSceneItemRequest returns a new DuplicateSceneItemRequest. +func NewDuplicateSceneItemRequest( + fromScene string, + toScene string, + item map[string]interface{}, + itemName string, + itemID int, +) DuplicateSceneItemRequest { + return DuplicateSceneItemRequest{ + fromScene, + toScene, + item, + itemName, + itemID, + _request{ + ID_: getMessageID(), + Type_: "DuplicateSceneItem", + err: make(chan error, 1), + }, + make(chan DuplicateSceneItemResponse, 1), + } +} + +// Send sends the request. +func (r *DuplicateSceneItemRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp DuplicateSceneItemResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r DuplicateSceneItemRequest) Receive() (DuplicateSceneItemResponse, error) { + if !r.sent { + return DuplicateSceneItemResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return DuplicateSceneItemResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return DuplicateSceneItemResponse{}, err + case <-time.After(receiveTimeout): + return DuplicateSceneItemResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r DuplicateSceneItemRequest) SendReceive(c *Client) (DuplicateSceneItemResponse, error) { + if err := r.Send(c); err != nil { + return DuplicateSceneItemResponse{}, err + } + return r.Receive() +} + +// DuplicateSceneItemResponse : Response for DuplicateSceneItemRequest. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#duplicatesceneitem +type DuplicateSceneItemResponse struct { + _response `json:",squash"` +} + +// GetSpecialSourcesRequest : Get configured special sources like Desktop Audio and Mic/Aux sources. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getspecialsources +type GetSpecialSourcesRequest struct { + _request `json:",squash"` + response chan GetSpecialSourcesResponse +} + +// NewGetSpecialSourcesRequest returns a new GetSpecialSourcesRequest. +func NewGetSpecialSourcesRequest() GetSpecialSourcesRequest { + return GetSpecialSourcesRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetSpecialSources", + err: make(chan error, 1), + }, + make(chan GetSpecialSourcesResponse, 1), + } +} + +// Send sends the request. +func (r *GetSpecialSourcesRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetSpecialSourcesResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetSpecialSourcesRequest) Receive() (GetSpecialSourcesResponse, error) { + if !r.sent { + return GetSpecialSourcesResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSpecialSourcesResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSpecialSourcesResponse{}, err + case <-time.After(receiveTimeout): + return GetSpecialSourcesResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetSpecialSourcesRequest) SendReceive(c *Client) (GetSpecialSourcesResponse, error) { + if err := r.Send(c); err != nil { + return GetSpecialSourcesResponse{}, err + } + return r.Receive() +} + +// GetSpecialSourcesResponse : Response for GetSpecialSourcesRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getspecialsources +type GetSpecialSourcesResponse struct { + // Name of the first Desktop Audio capture source. + // Required: No. + Desktop1 string `json:"desktop-1"` + // Name of the second Desktop Audio capture source. + // Required: No. + Desktop2 string `json:"desktop-2"` + // Name of the first Mic/Aux input source. + // Required: No. + Mic1 string `json:"mic-1"` + // Name of the second Mic/Aux input source. + // Required: No. + Mic2 string `json:"mic-2"` + // NAme of the third Mic/Aux input source. + // Required: No. + Mic3 string `json:"mic-3"` + _response `json:",squash"` +} + +// GetSourceFiltersRequest : List filters applied to a source. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsourcefilters +type GetSourceFiltersRequest struct { + // Source name. + // Required: Yes. + SourceName string `json:"sourceName"` + _request `json:",squash"` + response chan GetSourceFiltersResponse +} + +// NewGetSourceFiltersRequest returns a new GetSourceFiltersRequest. +func NewGetSourceFiltersRequest(sourceName string) GetSourceFiltersRequest { + return GetSourceFiltersRequest{ + sourceName, + _request{ + ID_: getMessageID(), + Type_: "GetSourceFilters", + err: make(chan error, 1), + }, + make(chan GetSourceFiltersResponse, 1), + } +} + +// Send sends the request. +func (r *GetSourceFiltersRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetSourceFiltersResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetSourceFiltersRequest) Receive() (GetSourceFiltersResponse, error) { + if !r.sent { + return GetSourceFiltersResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSourceFiltersResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetSourceFiltersResponse{}, err + case <-time.After(receiveTimeout): + return GetSourceFiltersResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetSourceFiltersRequest) SendReceive(c *Client) (GetSourceFiltersResponse, error) { + if err := r.Send(c); err != nil { + return GetSourceFiltersResponse{}, err + } + return r.Receive() +} + +// GetSourceFiltersResponse : Response for GetSourceFiltersRequest. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getsourcefilters +type GetSourceFiltersResponse struct { + // List of filters for the specified source. + // Required: Yes. + Filters []map[string]interface{} `json:"filters"` + // Filter type. + // Required: Yes. + FiltersType string `json:"filters.*.type"` + // Filter name. + // Required: Yes. + FiltersName string `json:"filters.*.name"` + // Filter settings. + // Required: Yes. + FiltersSettings map[string]interface{} `json:"filters.*.settings"` + _response `json:",squash"` +} + +// AddFilterToSourceRequest : Add a new filter to a source +// Available source types along with their settings properties are available from `GetSourceTypesList`. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#addfiltertosource +type AddFilterToSourceRequest struct { + // Name of the source on which the filter is added. + // Required: Yes. + SourceName string `json:"sourceName"` + // Name of the new filter. + // Required: Yes. + FilterName string `json:"filterName"` + // Filter type. + // Required: Yes. + FilterType string `json:"filterType"` + // Filter settings. + // Required: Yes. + FilterSettings map[string]interface{} `json:"filterSettings"` + _request `json:",squash"` + response chan AddFilterToSourceResponse +} + +// NewAddFilterToSourceRequest returns a new AddFilterToSourceRequest. +func NewAddFilterToSourceRequest( + sourceName string, + filterName string, + filterType string, + filterSettings map[string]interface{}, +) AddFilterToSourceRequest { + return AddFilterToSourceRequest{ + sourceName, + filterName, + filterType, + filterSettings, + _request{ + ID_: getMessageID(), + Type_: "AddFilterToSource", + err: make(chan error, 1), + }, + make(chan AddFilterToSourceResponse, 1), + } +} + +// Send sends the request. +func (r *AddFilterToSourceRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp AddFilterToSourceResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r AddFilterToSourceRequest) Receive() (AddFilterToSourceResponse, error) { + if !r.sent { + return AddFilterToSourceResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return AddFilterToSourceResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return AddFilterToSourceResponse{}, err + case <-time.After(receiveTimeout): + return AddFilterToSourceResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r AddFilterToSourceRequest) SendReceive(c *Client) (AddFilterToSourceResponse, error) { + if err := r.Send(c); err != nil { + return AddFilterToSourceResponse{}, err + } + return r.Receive() +} + +// AddFilterToSourceResponse : Response for AddFilterToSourceRequest. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#addfiltertosource +type AddFilterToSourceResponse struct { + _response `json:",squash"` +} + +// RemoveFilterFromSourceRequest : Remove a filter from a source. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#removefilterfromsource +type RemoveFilterFromSourceRequest struct { + // Name of the source from which the specified filter is removed. + // Required: Yes. + SourceName string `json:"sourceName"` + // Name of the filter to remove. + // Required: Yes. + FilterName string `json:"filterName"` + _request `json:",squash"` + response chan RemoveFilterFromSourceResponse +} + +// NewRemoveFilterFromSourceRequest returns a new RemoveFilterFromSourceRequest. +func NewRemoveFilterFromSourceRequest( + sourceName string, + filterName string, +) RemoveFilterFromSourceRequest { + return RemoveFilterFromSourceRequest{ + sourceName, + filterName, + _request{ + ID_: getMessageID(), + Type_: "RemoveFilterFromSource", + err: make(chan error, 1), + }, + make(chan RemoveFilterFromSourceResponse, 1), + } +} + +// Send sends the request. +func (r *RemoveFilterFromSourceRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp RemoveFilterFromSourceResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r RemoveFilterFromSourceRequest) Receive() (RemoveFilterFromSourceResponse, error) { + if !r.sent { + return RemoveFilterFromSourceResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return RemoveFilterFromSourceResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return RemoveFilterFromSourceResponse{}, err + case <-time.After(receiveTimeout): + return RemoveFilterFromSourceResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r RemoveFilterFromSourceRequest) SendReceive(c *Client) (RemoveFilterFromSourceResponse, error) { + if err := r.Send(c); err != nil { + return RemoveFilterFromSourceResponse{}, err + } + return r.Receive() +} + +// RemoveFilterFromSourceResponse : Response for RemoveFilterFromSourceRequest. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#removefilterfromsource +type RemoveFilterFromSourceResponse struct { + _response `json:",squash"` +} + +// ReorderSourceFilterRequest : Move a filter in the chain (absolute index positioning). +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#reordersourcefilter +type ReorderSourceFilterRequest struct { + // Name of the source to which the filter belongs. + // Required: Yes. + SourceName string `json:"sourceName"` + // Name of the filter to reorder. + // Required: Yes. + FilterName string `json:"filterName"` + // Desired position of the filter in the chain. + // Required: Yes. + NewIndex int `json:"newIndex"` + _request `json:",squash"` + response chan ReorderSourceFilterResponse +} + +// NewReorderSourceFilterRequest returns a new ReorderSourceFilterRequest. +func NewReorderSourceFilterRequest( + sourceName string, + filterName string, + newIndex int, +) ReorderSourceFilterRequest { + return ReorderSourceFilterRequest{ + sourceName, + filterName, + newIndex, + _request{ + ID_: getMessageID(), + Type_: "ReorderSourceFilter", + err: make(chan error, 1), + }, + make(chan ReorderSourceFilterResponse, 1), + } +} + +// Send sends the request. +func (r *ReorderSourceFilterRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp ReorderSourceFilterResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r ReorderSourceFilterRequest) Receive() (ReorderSourceFilterResponse, error) { + if !r.sent { + return ReorderSourceFilterResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ReorderSourceFilterResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ReorderSourceFilterResponse{}, err + case <-time.After(receiveTimeout): + return ReorderSourceFilterResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r ReorderSourceFilterRequest) SendReceive(c *Client) (ReorderSourceFilterResponse, error) { + if err := r.Send(c); err != nil { + return ReorderSourceFilterResponse{}, err + } + return r.Receive() +} + +// ReorderSourceFilterResponse : Response for ReorderSourceFilterRequest. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#reordersourcefilter +type ReorderSourceFilterResponse struct { + _response `json:",squash"` +} + +// MoveSourceFilterRequest : Move a filter in the chain (relative positioning). +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#movesourcefilter +type MoveSourceFilterRequest struct { + // Name of the source to which the filter belongs. + // Required: Yes. + SourceName string `json:"sourceName"` + // Name of the filter to reorder. + // Required: Yes. + FilterName string `json:"filterName"` + // How to move the filter around in the source's filter chain. + // Either "up", "down", "top" or "bottom". + // Required: Yes. + MovementType string `json:"movementType"` + _request `json:",squash"` + response chan MoveSourceFilterResponse +} + +// NewMoveSourceFilterRequest returns a new MoveSourceFilterRequest. +func NewMoveSourceFilterRequest( + sourceName string, + filterName string, + movementType string, +) MoveSourceFilterRequest { + return MoveSourceFilterRequest{ + sourceName, + filterName, + movementType, + _request{ + ID_: getMessageID(), + Type_: "MoveSourceFilter", + err: make(chan error, 1), + }, + make(chan MoveSourceFilterResponse, 1), + } +} + +// Send sends the request. +func (r *MoveSourceFilterRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp MoveSourceFilterResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r MoveSourceFilterRequest) Receive() (MoveSourceFilterResponse, error) { + if !r.sent { + return MoveSourceFilterResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return MoveSourceFilterResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return MoveSourceFilterResponse{}, err + case <-time.After(receiveTimeout): + return MoveSourceFilterResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r MoveSourceFilterRequest) SendReceive(c *Client) (MoveSourceFilterResponse, error) { + if err := r.Send(c); err != nil { + return MoveSourceFilterResponse{}, err + } + return r.Receive() +} + +// MoveSourceFilterResponse : Response for MoveSourceFilterRequest. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#movesourcefilter +type MoveSourceFilterResponse struct { + _response `json:",squash"` +} + +// SetSourceFilterSettingsRequest : Update settings of a filter. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsourcefiltersettings +type SetSourceFilterSettingsRequest struct { + // Name of the source to which the filter belongs. + // Required: Yes. + SourceName string `json:"sourceName"` + // Name of the filter to reconfigure. + // Required: Yes. + FilterName string `json:"filterName"` + // New settings. + // These will be merged to the current filter settings. + // Required: Yes. + FilterSettings map[string]interface{} `json:"filterSettings"` + _request `json:",squash"` + response chan SetSourceFilterSettingsResponse +} + +// NewSetSourceFilterSettingsRequest returns a new SetSourceFilterSettingsRequest. +func NewSetSourceFilterSettingsRequest( + sourceName string, + filterName string, + filterSettings map[string]interface{}, +) SetSourceFilterSettingsRequest { + return SetSourceFilterSettingsRequest{ + sourceName, + filterName, + filterSettings, + _request{ + ID_: getMessageID(), + Type_: "SetSourceFilterSettings", + err: make(chan error, 1), + }, + make(chan SetSourceFilterSettingsResponse, 1), + } +} + +// Send sends the request. +func (r *SetSourceFilterSettingsRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetSourceFilterSettingsResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetSourceFilterSettingsRequest) Receive() (SetSourceFilterSettingsResponse, error) { + if !r.sent { + return SetSourceFilterSettingsResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSourceFilterSettingsResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetSourceFilterSettingsResponse{}, err + case <-time.After(receiveTimeout): + return SetSourceFilterSettingsResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetSourceFilterSettingsRequest) SendReceive(c *Client) (SetSourceFilterSettingsResponse, error) { + if err := r.Send(c); err != nil { + return SetSourceFilterSettingsResponse{}, err + } + return r.Receive() +} + +// SetSourceFilterSettingsResponse : Response for SetSourceFilterSettingsRequest. +// +// Since obs-websocket version: Unreleased. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setsourcefiltersettings +type SetSourceFilterSettingsResponse struct { + _response `json:",squash"` +} diff --git a/obsws/requests_streaming.go b/obsws/requests_streaming.go new file mode 100644 index 0000000..fe11ae1 --- /dev/null +++ b/obsws/requests_streaming.go @@ -0,0 +1,757 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// GetStreamingStatusRequest : Get current streaming and recording status. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getstreamingstatus +type GetStreamingStatusRequest struct { + _request `json:",squash"` + response chan GetStreamingStatusResponse +} + +// NewGetStreamingStatusRequest returns a new GetStreamingStatusRequest. +func NewGetStreamingStatusRequest() GetStreamingStatusRequest { + return GetStreamingStatusRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetStreamingStatus", + err: make(chan error, 1), + }, + make(chan GetStreamingStatusResponse, 1), + } +} + +// Send sends the request. +func (r *GetStreamingStatusRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetStreamingStatusResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetStreamingStatusRequest) Receive() (GetStreamingStatusResponse, error) { + if !r.sent { + return GetStreamingStatusResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetStreamingStatusResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetStreamingStatusResponse{}, err + case <-time.After(receiveTimeout): + return GetStreamingStatusResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetStreamingStatusRequest) SendReceive(c *Client) (GetStreamingStatusResponse, error) { + if err := r.Send(c); err != nil { + return GetStreamingStatusResponse{}, err + } + return r.Receive() +} + +// GetStreamingStatusResponse : Response for GetStreamingStatusRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getstreamingstatus +type GetStreamingStatusResponse struct { + // Current streaming status. + // Required: Yes. + Streaming bool `json:"streaming"` + // Current recording status. + // Required: Yes. + Recording bool `json:"recording"` + // Time elapsed since streaming started (only present if currently streaming). + // Required: No. + StreamTimecode string `json:"stream-timecode"` + // Time elapsed since recording started (only present if currently recording). + // Required: No. + RecTimecode string `json:"rec-timecode"` + // Always false. + // Retrocompatibility with OBSRemote. + // Required: Yes. + PreviewOnly bool `json:"preview-only"` + _response `json:",squash"` +} + +// StartStopStreamingRequest : Toggle streaming on or off. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startstopstreaming +type StartStopStreamingRequest struct { + _request `json:",squash"` + response chan StartStopStreamingResponse +} + +// NewStartStopStreamingRequest returns a new StartStopStreamingRequest. +func NewStartStopStreamingRequest() StartStopStreamingRequest { + return StartStopStreamingRequest{ + _request{ + ID_: getMessageID(), + Type_: "StartStopStreaming", + err: make(chan error, 1), + }, + make(chan StartStopStreamingResponse, 1), + } +} + +// Send sends the request. +func (r *StartStopStreamingRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp StartStopStreamingResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r StartStopStreamingRequest) Receive() (StartStopStreamingResponse, error) { + if !r.sent { + return StartStopStreamingResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartStopStreamingResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartStopStreamingResponse{}, err + case <-time.After(receiveTimeout): + return StartStopStreamingResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r StartStopStreamingRequest) SendReceive(c *Client) (StartStopStreamingResponse, error) { + if err := r.Send(c); err != nil { + return StartStopStreamingResponse{}, err + } + return r.Receive() +} + +// StartStopStreamingResponse : Response for StartStopStreamingRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startstopstreaming +type StartStopStreamingResponse struct { + _response `json:",squash"` +} + +// StartStreamingRequest : Start streaming. +// Will return an `error` if streaming is already active. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startstreaming +type StartStreamingRequest struct { + // Special stream configuration. + // Please note: these won't be saved to OBS' configuration. + // Required: No. + Stream map[string]interface{} `json:"stream"` + // If specified ensures the type of stream matches the given type (usually 'rtmp_custom' or 'rtmp_common'). + // If the currently configured stream type does not match the given stream type, all settings must be specified in the `settings` object or an error will occur when starting the stream. + // Required: No. + StreamType string `json:"stream.type"` + // Adds the given object parameters as encoded query string parameters to the 'key' of the RTMP stream. + // Used to pass data to the RTMP service about the streaming. + // May be any String, Numeric, or Boolean field. + // Required: No. + StreamMetadata map[string]interface{} `json:"stream.metadata"` + // Settings for the stream. + // Required: No. + StreamSettings map[string]interface{} `json:"stream.settings"` + // The publish URL. + // Required: No. + StreamSettingsServer string `json:"stream.settings.server"` + // The publish key of the stream. + // Required: No. + StreamSettingsKey string `json:"stream.settings.key"` + // Indicates whether authentication should be used when connecting to the streaming server. + // Required: No. + StreamSettingsUseAuth bool `json:"stream.settings.use-auth"` + // If authentication is enabled, the username for the streaming server. + // Ignored if `use-auth` is not set to `true`. + // Required: No. + StreamSettingsUsername string `json:"stream.settings.username"` + // If authentication is enabled, the password for the streaming server. + // Ignored if `use-auth` is not set to `true`. + // Required: No. + StreamSettingsPassword string `json:"stream.settings.password"` + _request `json:",squash"` + response chan StartStreamingResponse +} + +// NewStartStreamingRequest returns a new StartStreamingRequest. +func NewStartStreamingRequest( + stream map[string]interface{}, + streamType string, + streamMetadata map[string]interface{}, + streamSettings map[string]interface{}, + streamSettingsServer string, + streamSettingsKey string, + streamSettingsUseAuth bool, + streamSettingsUsername string, + streamSettingsPassword string, +) StartStreamingRequest { + return StartStreamingRequest{ + stream, + streamType, + streamMetadata, + streamSettings, + streamSettingsServer, + streamSettingsKey, + streamSettingsUseAuth, + streamSettingsUsername, + streamSettingsPassword, + _request{ + ID_: getMessageID(), + Type_: "StartStreaming", + err: make(chan error, 1), + }, + make(chan StartStreamingResponse, 1), + } +} + +// Send sends the request. +func (r *StartStreamingRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp StartStreamingResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r StartStreamingRequest) Receive() (StartStreamingResponse, error) { + if !r.sent { + return StartStreamingResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartStreamingResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StartStreamingResponse{}, err + case <-time.After(receiveTimeout): + return StartStreamingResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r StartStreamingRequest) SendReceive(c *Client) (StartStreamingResponse, error) { + if err := r.Send(c); err != nil { + return StartStreamingResponse{}, err + } + return r.Receive() +} + +// StartStreamingResponse : Response for StartStreamingRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#startstreaming +type StartStreamingResponse struct { + _response `json:",squash"` +} + +// StopStreamingRequest : Stop streaming. +// Will return an `error` if streaming is not active. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#stopstreaming +type StopStreamingRequest struct { + _request `json:",squash"` + response chan StopStreamingResponse +} + +// NewStopStreamingRequest returns a new StopStreamingRequest. +func NewStopStreamingRequest() StopStreamingRequest { + return StopStreamingRequest{ + _request{ + ID_: getMessageID(), + Type_: "StopStreaming", + err: make(chan error, 1), + }, + make(chan StopStreamingResponse, 1), + } +} + +// Send sends the request. +func (r *StopStreamingRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp StopStreamingResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r StopStreamingRequest) Receive() (StopStreamingResponse, error) { + if !r.sent { + return StopStreamingResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StopStreamingResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return StopStreamingResponse{}, err + case <-time.After(receiveTimeout): + return StopStreamingResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r StopStreamingRequest) SendReceive(c *Client) (StopStreamingResponse, error) { + if err := r.Send(c); err != nil { + return StopStreamingResponse{}, err + } + return r.Receive() +} + +// StopStreamingResponse : Response for StopStreamingRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#stopstreaming +type StopStreamingResponse struct { + _response `json:",squash"` +} + +// SetStreamSettingsRequest : Sets one or more attributes of the current streaming server settings +// Any options not passed will remain unchanged +// Returns the updated settings in response +// If 'type' is different than the current streaming service type, all settings are required +// Returns the full settings of the stream (the same as GetStreamSettings). +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setstreamsettings +type SetStreamSettingsRequest struct { + // The type of streaming service configuration, usually `rtmp_custom` or `rtmp_common`. + // Required: Yes. + StreamType string `json:"type"` + // The actual settings of the stream. + // Required: Yes. + Settings map[string]interface{} `json:"settings"` + // The publish URL. + // Required: No. + SettingsServer string `json:"settings.server"` + // The publish key. + // Required: No. + SettingsKey string `json:"settings.key"` + // Indicates whether authentication should be used when connecting to the streaming server. + // Required: No. + SettingsUseAuth bool `json:"settings.use-auth"` + // The username for the streaming service. + // Required: No. + SettingsUsername string `json:"settings.username"` + // The password for the streaming service. + // Required: No. + SettingsPassword string `json:"settings.password"` + // Persist the settings to disk. + // Required: Yes. + Save bool `json:"save"` + _request `json:",squash"` + response chan SetStreamSettingsResponse +} + +// NewSetStreamSettingsRequest returns a new SetStreamSettingsRequest. +func NewSetStreamSettingsRequest( + _type string, + settings map[string]interface{}, + settingsServer string, + settingsKey string, + settingsUseAuth bool, + settingsUsername string, + settingsPassword string, + save bool, +) SetStreamSettingsRequest { + return SetStreamSettingsRequest{ + _type, + settings, + settingsServer, + settingsKey, + settingsUseAuth, + settingsUsername, + settingsPassword, + save, + _request{ + ID_: getMessageID(), + Type_: "SetStreamSettings", + err: make(chan error, 1), + }, + make(chan SetStreamSettingsResponse, 1), + } +} + +// Send sends the request. +func (r *SetStreamSettingsRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetStreamSettingsResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetStreamSettingsRequest) Receive() (SetStreamSettingsResponse, error) { + if !r.sent { + return SetStreamSettingsResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetStreamSettingsResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetStreamSettingsResponse{}, err + case <-time.After(receiveTimeout): + return SetStreamSettingsResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetStreamSettingsRequest) SendReceive(c *Client) (SetStreamSettingsResponse, error) { + if err := r.Send(c); err != nil { + return SetStreamSettingsResponse{}, err + } + return r.Receive() +} + +// SetStreamSettingsResponse : Response for SetStreamSettingsRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setstreamsettings +type SetStreamSettingsResponse struct { + _response `json:",squash"` +} + +// GetStreamSettingsRequest : Get the current streaming server settings. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getstreamsettings +type GetStreamSettingsRequest struct { + _request `json:",squash"` + response chan GetStreamSettingsResponse +} + +// NewGetStreamSettingsRequest returns a new GetStreamSettingsRequest. +func NewGetStreamSettingsRequest() GetStreamSettingsRequest { + return GetStreamSettingsRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetStreamSettings", + err: make(chan error, 1), + }, + make(chan GetStreamSettingsResponse, 1), + } +} + +// Send sends the request. +func (r *GetStreamSettingsRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetStreamSettingsResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetStreamSettingsRequest) Receive() (GetStreamSettingsResponse, error) { + if !r.sent { + return GetStreamSettingsResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetStreamSettingsResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetStreamSettingsResponse{}, err + case <-time.After(receiveTimeout): + return GetStreamSettingsResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetStreamSettingsRequest) SendReceive(c *Client) (GetStreamSettingsResponse, error) { + if err := r.Send(c); err != nil { + return GetStreamSettingsResponse{}, err + } + return r.Receive() +} + +// GetStreamSettingsResponse : Response for GetStreamSettingsRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getstreamsettings +type GetStreamSettingsResponse struct { + // The type of streaming service configuration. + // Possible values: 'rtmp_custom' or 'rtmp_common'. + // Required: Yes. + Type string `json:"type"` + // Stream settings object. + // Required: Yes. + Settings map[string]interface{} `json:"settings"` + // The publish URL. + // Required: Yes. + SettingsServer string `json:"settings.server"` + // The publish key of the stream. + // Required: Yes. + SettingsKey string `json:"settings.key"` + // Indicates whether authentication should be used when connecting to the streaming server. + // Required: Yes. + SettingsUseAuth bool `json:"settings.use-auth"` + // The username to use when accessing the streaming server. + // Only present if `use-auth` is `true`. + // Required: Yes. + SettingsUsername string `json:"settings.username"` + // The password to use when accessing the streaming server. + // Only present if `use-auth` is `true`. + // Required: Yes. + SettingsPassword string `json:"settings.password"` + _response `json:",squash"` +} + +// SaveStreamSettingsRequest : Save the current streaming server settings to disk. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#savestreamsettings +type SaveStreamSettingsRequest struct { + _request `json:",squash"` + response chan SaveStreamSettingsResponse +} + +// NewSaveStreamSettingsRequest returns a new SaveStreamSettingsRequest. +func NewSaveStreamSettingsRequest() SaveStreamSettingsRequest { + return SaveStreamSettingsRequest{ + _request{ + ID_: getMessageID(), + Type_: "SaveStreamSettings", + err: make(chan error, 1), + }, + make(chan SaveStreamSettingsResponse, 1), + } +} + +// Send sends the request. +func (r *SaveStreamSettingsRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SaveStreamSettingsResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SaveStreamSettingsRequest) Receive() (SaveStreamSettingsResponse, error) { + if !r.sent { + return SaveStreamSettingsResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SaveStreamSettingsResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SaveStreamSettingsResponse{}, err + case <-time.After(receiveTimeout): + return SaveStreamSettingsResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SaveStreamSettingsRequest) SendReceive(c *Client) (SaveStreamSettingsResponse, error) { + if err := r.Send(c); err != nil { + return SaveStreamSettingsResponse{}, err + } + return r.Receive() +} + +// SaveStreamSettingsResponse : Response for SaveStreamSettingsRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#savestreamsettings +type SaveStreamSettingsResponse struct { + _response `json:",squash"` +} diff --git a/obsws/requests_studio_mode.go b/obsws/requests_studio_mode.go new file mode 100644 index 0000000..1975a92 --- /dev/null +++ b/obsws/requests_studio_mode.go @@ -0,0 +1,650 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// GetStudioModeStatusRequest : Indicates if Studio Mode is currently enabled. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getstudiomodestatus +type GetStudioModeStatusRequest struct { + _request `json:",squash"` + response chan GetStudioModeStatusResponse +} + +// NewGetStudioModeStatusRequest returns a new GetStudioModeStatusRequest. +func NewGetStudioModeStatusRequest() GetStudioModeStatusRequest { + return GetStudioModeStatusRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetStudioModeStatus", + err: make(chan error, 1), + }, + make(chan GetStudioModeStatusResponse, 1), + } +} + +// Send sends the request. +func (r *GetStudioModeStatusRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetStudioModeStatusResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetStudioModeStatusRequest) Receive() (GetStudioModeStatusResponse, error) { + if !r.sent { + return GetStudioModeStatusResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetStudioModeStatusResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetStudioModeStatusResponse{}, err + case <-time.After(receiveTimeout): + return GetStudioModeStatusResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetStudioModeStatusRequest) SendReceive(c *Client) (GetStudioModeStatusResponse, error) { + if err := r.Send(c); err != nil { + return GetStudioModeStatusResponse{}, err + } + return r.Receive() +} + +// GetStudioModeStatusResponse : Response for GetStudioModeStatusRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getstudiomodestatus +type GetStudioModeStatusResponse struct { + // Indicates if Studio Mode is enabled. + // Required: Yes. + StudioMode bool `json:"studio-mode"` + _response `json:",squash"` +} + +// GetPreviewSceneRequest : Get the name of the currently previewed scene and its list of sources. +// Will return an `error` if Studio Mode is not enabled. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getpreviewscene +type GetPreviewSceneRequest struct { + _request `json:",squash"` + response chan GetPreviewSceneResponse +} + +// NewGetPreviewSceneRequest returns a new GetPreviewSceneRequest. +func NewGetPreviewSceneRequest() GetPreviewSceneRequest { + return GetPreviewSceneRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetPreviewScene", + err: make(chan error, 1), + }, + make(chan GetPreviewSceneResponse, 1), + } +} + +// Send sends the request. +func (r *GetPreviewSceneRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetPreviewSceneResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetPreviewSceneRequest) Receive() (GetPreviewSceneResponse, error) { + if !r.sent { + return GetPreviewSceneResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetPreviewSceneResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetPreviewSceneResponse{}, err + case <-time.After(receiveTimeout): + return GetPreviewSceneResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetPreviewSceneRequest) SendReceive(c *Client) (GetPreviewSceneResponse, error) { + if err := r.Send(c); err != nil { + return GetPreviewSceneResponse{}, err + } + return r.Receive() +} + +// GetPreviewSceneResponse : Response for GetPreviewSceneRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getpreviewscene +type GetPreviewSceneResponse struct { + // The name of the active preview scene. + // Required: Yes. + Name string `json:"name"` + // Required: Yes. + Sources []map[string]interface{} `json:"sources"` + _response `json:",squash"` +} + +// SetPreviewSceneRequest : Set the active preview scene. +// Will return an `error` if Studio Mode is not enabled. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setpreviewscene +type SetPreviewSceneRequest struct { + // The name of the scene to preview. + // Required: Yes. + SceneName string `json:"scene-name"` + _request `json:",squash"` + response chan SetPreviewSceneResponse +} + +// NewSetPreviewSceneRequest returns a new SetPreviewSceneRequest. +func NewSetPreviewSceneRequest(sceneName string) SetPreviewSceneRequest { + return SetPreviewSceneRequest{ + sceneName, + _request{ + ID_: getMessageID(), + Type_: "SetPreviewScene", + err: make(chan error, 1), + }, + make(chan SetPreviewSceneResponse, 1), + } +} + +// Send sends the request. +func (r *SetPreviewSceneRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetPreviewSceneResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetPreviewSceneRequest) Receive() (SetPreviewSceneResponse, error) { + if !r.sent { + return SetPreviewSceneResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetPreviewSceneResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetPreviewSceneResponse{}, err + case <-time.After(receiveTimeout): + return SetPreviewSceneResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetPreviewSceneRequest) SendReceive(c *Client) (SetPreviewSceneResponse, error) { + if err := r.Send(c); err != nil { + return SetPreviewSceneResponse{}, err + } + return r.Receive() +} + +// SetPreviewSceneResponse : Response for SetPreviewSceneRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setpreviewscene +type SetPreviewSceneResponse struct { + _response `json:",squash"` +} + +// TransitionToProgramRequest : Transitions the currently previewed scene to the main output. +// Will return an `error` if Studio Mode is not enabled. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#transitiontoprogram +type TransitionToProgramRequest struct { + // Change the active transition before switching scenes. + // Defaults to the active transition. + // Required: No. + WithTransition map[string]interface{} `json:"with-transition"` + // Name of the transition. + // Required: Yes. + WithTransitionName string `json:"with-transition.name"` + // Transition duration (in milliseconds). + // Required: No. + WithTransitionDuration int `json:"with-transition.duration"` + _request `json:",squash"` + response chan TransitionToProgramResponse +} + +// NewTransitionToProgramRequest returns a new TransitionToProgramRequest. +func NewTransitionToProgramRequest( + withTransition map[string]interface{}, + withTransitionName string, + withTransitionDuration int, +) TransitionToProgramRequest { + return TransitionToProgramRequest{ + withTransition, + withTransitionName, + withTransitionDuration, + _request{ + ID_: getMessageID(), + Type_: "TransitionToProgram", + err: make(chan error, 1), + }, + make(chan TransitionToProgramResponse, 1), + } +} + +// Send sends the request. +func (r *TransitionToProgramRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp TransitionToProgramResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r TransitionToProgramRequest) Receive() (TransitionToProgramResponse, error) { + if !r.sent { + return TransitionToProgramResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return TransitionToProgramResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return TransitionToProgramResponse{}, err + case <-time.After(receiveTimeout): + return TransitionToProgramResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r TransitionToProgramRequest) SendReceive(c *Client) (TransitionToProgramResponse, error) { + if err := r.Send(c); err != nil { + return TransitionToProgramResponse{}, err + } + return r.Receive() +} + +// TransitionToProgramResponse : Response for TransitionToProgramRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#transitiontoprogram +type TransitionToProgramResponse struct { + _response `json:",squash"` +} + +// EnableStudioModeRequest : Enables Studio Mode. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#enablestudiomode +type EnableStudioModeRequest struct { + _request `json:",squash"` + response chan EnableStudioModeResponse +} + +// NewEnableStudioModeRequest returns a new EnableStudioModeRequest. +func NewEnableStudioModeRequest() EnableStudioModeRequest { + return EnableStudioModeRequest{ + _request{ + ID_: getMessageID(), + Type_: "EnableStudioMode", + err: make(chan error, 1), + }, + make(chan EnableStudioModeResponse, 1), + } +} + +// Send sends the request. +func (r *EnableStudioModeRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp EnableStudioModeResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r EnableStudioModeRequest) Receive() (EnableStudioModeResponse, error) { + if !r.sent { + return EnableStudioModeResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return EnableStudioModeResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return EnableStudioModeResponse{}, err + case <-time.After(receiveTimeout): + return EnableStudioModeResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r EnableStudioModeRequest) SendReceive(c *Client) (EnableStudioModeResponse, error) { + if err := r.Send(c); err != nil { + return EnableStudioModeResponse{}, err + } + return r.Receive() +} + +// EnableStudioModeResponse : Response for EnableStudioModeRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#enablestudiomode +type EnableStudioModeResponse struct { + _response `json:",squash"` +} + +// DisableStudioModeRequest : Disables Studio Mode. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#disablestudiomode +type DisableStudioModeRequest struct { + _request `json:",squash"` + response chan DisableStudioModeResponse +} + +// NewDisableStudioModeRequest returns a new DisableStudioModeRequest. +func NewDisableStudioModeRequest() DisableStudioModeRequest { + return DisableStudioModeRequest{ + _request{ + ID_: getMessageID(), + Type_: "DisableStudioMode", + err: make(chan error, 1), + }, + make(chan DisableStudioModeResponse, 1), + } +} + +// Send sends the request. +func (r *DisableStudioModeRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp DisableStudioModeResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r DisableStudioModeRequest) Receive() (DisableStudioModeResponse, error) { + if !r.sent { + return DisableStudioModeResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return DisableStudioModeResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return DisableStudioModeResponse{}, err + case <-time.After(receiveTimeout): + return DisableStudioModeResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r DisableStudioModeRequest) SendReceive(c *Client) (DisableStudioModeResponse, error) { + if err := r.Send(c); err != nil { + return DisableStudioModeResponse{}, err + } + return r.Receive() +} + +// DisableStudioModeResponse : Response for DisableStudioModeRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#disablestudiomode +type DisableStudioModeResponse struct { + _response `json:",squash"` +} + +// ToggleStudioModeRequest : Toggles Studio Mode. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#togglestudiomode +type ToggleStudioModeRequest struct { + _request `json:",squash"` + response chan ToggleStudioModeResponse +} + +// NewToggleStudioModeRequest returns a new ToggleStudioModeRequest. +func NewToggleStudioModeRequest() ToggleStudioModeRequest { + return ToggleStudioModeRequest{ + _request{ + ID_: getMessageID(), + Type_: "ToggleStudioMode", + err: make(chan error, 1), + }, + make(chan ToggleStudioModeResponse, 1), + } +} + +// Send sends the request. +func (r *ToggleStudioModeRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp ToggleStudioModeResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r ToggleStudioModeRequest) Receive() (ToggleStudioModeResponse, error) { + if !r.sent { + return ToggleStudioModeResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ToggleStudioModeResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return ToggleStudioModeResponse{}, err + case <-time.After(receiveTimeout): + return ToggleStudioModeResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r ToggleStudioModeRequest) SendReceive(c *Client) (ToggleStudioModeResponse, error) { + if err := r.Send(c); err != nil { + return ToggleStudioModeResponse{}, err + } + return r.Receive() +} + +// ToggleStudioModeResponse : Response for ToggleStudioModeRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#togglestudiomode +type ToggleStudioModeResponse struct { + _response `json:",squash"` +} diff --git a/obsws/requests_transitions.go b/obsws/requests_transitions.go new file mode 100644 index 0000000..633e6b2 --- /dev/null +++ b/obsws/requests_transitions.go @@ -0,0 +1,470 @@ +package obsws + +import ( + "errors" + "time" +) + +// This file is automatically generated. +// https://github.com/christopher-dG/go-obs-websocket/blob/master/codegen/protocol.py + +// GetTransitionListRequest : List of all transitions available in the frontend's dropdown menu. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#gettransitionlist +type GetTransitionListRequest struct { + _request `json:",squash"` + response chan GetTransitionListResponse +} + +// NewGetTransitionListRequest returns a new GetTransitionListRequest. +func NewGetTransitionListRequest() GetTransitionListRequest { + return GetTransitionListRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetTransitionList", + err: make(chan error, 1), + }, + make(chan GetTransitionListResponse, 1), + } +} + +// Send sends the request. +func (r *GetTransitionListRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetTransitionListResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetTransitionListRequest) Receive() (GetTransitionListResponse, error) { + if !r.sent { + return GetTransitionListResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetTransitionListResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetTransitionListResponse{}, err + case <-time.After(receiveTimeout): + return GetTransitionListResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetTransitionListRequest) SendReceive(c *Client) (GetTransitionListResponse, error) { + if err := r.Send(c); err != nil { + return GetTransitionListResponse{}, err + } + return r.Receive() +} + +// GetTransitionListResponse : Response for GetTransitionListRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#gettransitionlist +type GetTransitionListResponse struct { + // Name of the currently active transition. + // Required: Yes. + CurrentTransition string `json:"current-transition"` + // List of transitions. + // Required: Yes. + Transitions interface{} `json:"transitions"` + // Name of the transition. + // Required: Yes. + TransitionsName string `json:"transitions[].name"` + _response `json:",squash"` +} + +// GetCurrentTransitionRequest : Get the name of the currently selected transition in the frontend's dropdown menu. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getcurrenttransition +type GetCurrentTransitionRequest struct { + _request `json:",squash"` + response chan GetCurrentTransitionResponse +} + +// NewGetCurrentTransitionRequest returns a new GetCurrentTransitionRequest. +func NewGetCurrentTransitionRequest() GetCurrentTransitionRequest { + return GetCurrentTransitionRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetCurrentTransition", + err: make(chan error, 1), + }, + make(chan GetCurrentTransitionResponse, 1), + } +} + +// Send sends the request. +func (r *GetCurrentTransitionRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetCurrentTransitionResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetCurrentTransitionRequest) Receive() (GetCurrentTransitionResponse, error) { + if !r.sent { + return GetCurrentTransitionResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetCurrentTransitionResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetCurrentTransitionResponse{}, err + case <-time.After(receiveTimeout): + return GetCurrentTransitionResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetCurrentTransitionRequest) SendReceive(c *Client) (GetCurrentTransitionResponse, error) { + if err := r.Send(c); err != nil { + return GetCurrentTransitionResponse{}, err + } + return r.Receive() +} + +// GetCurrentTransitionResponse : Response for GetCurrentTransitionRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#getcurrenttransition +type GetCurrentTransitionResponse struct { + // Name of the selected transition. + // Required: Yes. + Name string `json:"name"` + // Transition duration (in milliseconds) if supported by the transition. + // Required: No. + Duration int `json:"duration"` + _response `json:",squash"` +} + +// SetCurrentTransitionRequest : Set the active transition. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setcurrenttransition +type SetCurrentTransitionRequest struct { + // The name of the transition. + // Required: Yes. + TransitionName string `json:"transition-name"` + _request `json:",squash"` + response chan SetCurrentTransitionResponse +} + +// NewSetCurrentTransitionRequest returns a new SetCurrentTransitionRequest. +func NewSetCurrentTransitionRequest(transitionName string) SetCurrentTransitionRequest { + return SetCurrentTransitionRequest{ + transitionName, + _request{ + ID_: getMessageID(), + Type_: "SetCurrentTransition", + err: make(chan error, 1), + }, + make(chan SetCurrentTransitionResponse, 1), + } +} + +// Send sends the request. +func (r *SetCurrentTransitionRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetCurrentTransitionResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetCurrentTransitionRequest) Receive() (SetCurrentTransitionResponse, error) { + if !r.sent { + return SetCurrentTransitionResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetCurrentTransitionResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetCurrentTransitionResponse{}, err + case <-time.After(receiveTimeout): + return SetCurrentTransitionResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetCurrentTransitionRequest) SendReceive(c *Client) (SetCurrentTransitionResponse, error) { + if err := r.Send(c); err != nil { + return SetCurrentTransitionResponse{}, err + } + return r.Receive() +} + +// SetCurrentTransitionResponse : Response for SetCurrentTransitionRequest. +// +// Since obs-websocket version: 0.3. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#setcurrenttransition +type SetCurrentTransitionResponse struct { + _response `json:",squash"` +} + +// SetTransitionDurationRequest : Set the duration of the currently selected transition if supported. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#settransitionduration +type SetTransitionDurationRequest struct { + // Desired duration of the transition (in milliseconds). + // Required: Yes. + Duration int `json:"duration"` + _request `json:",squash"` + response chan SetTransitionDurationResponse +} + +// NewSetTransitionDurationRequest returns a new SetTransitionDurationRequest. +func NewSetTransitionDurationRequest(duration int) SetTransitionDurationRequest { + return SetTransitionDurationRequest{ + duration, + _request{ + ID_: getMessageID(), + Type_: "SetTransitionDuration", + err: make(chan error, 1), + }, + make(chan SetTransitionDurationResponse, 1), + } +} + +// Send sends the request. +func (r *SetTransitionDurationRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp SetTransitionDurationResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r SetTransitionDurationRequest) Receive() (SetTransitionDurationResponse, error) { + if !r.sent { + return SetTransitionDurationResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetTransitionDurationResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return SetTransitionDurationResponse{}, err + case <-time.After(receiveTimeout): + return SetTransitionDurationResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r SetTransitionDurationRequest) SendReceive(c *Client) (SetTransitionDurationResponse, error) { + if err := r.Send(c); err != nil { + return SetTransitionDurationResponse{}, err + } + return r.Receive() +} + +// SetTransitionDurationResponse : Response for SetTransitionDurationRequest. +// +// Since obs-websocket version: 4.0.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#settransitionduration +type SetTransitionDurationResponse struct { + _response `json:",squash"` +} + +// GetTransitionDurationRequest : Get the duration of the currently selected transition if supported. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#gettransitionduration +type GetTransitionDurationRequest struct { + _request `json:",squash"` + response chan GetTransitionDurationResponse +} + +// NewGetTransitionDurationRequest returns a new GetTransitionDurationRequest. +func NewGetTransitionDurationRequest() GetTransitionDurationRequest { + return GetTransitionDurationRequest{ + _request{ + ID_: getMessageID(), + Type_: "GetTransitionDuration", + err: make(chan error, 1), + }, + make(chan GetTransitionDurationResponse, 1), + } +} + +// Send sends the request. +func (r *GetTransitionDurationRequest) Send(c *Client) error { + if r.sent { + return ErrAlreadySent + } + future, err := c.sendRequest(r) + if err != nil { + return err + } + r.sent = true + go func() { + m := <-future + var resp GetTransitionDurationResponse + if err = mapToStruct(m, &resp); err != nil { + r.err <- err + } else if resp.Status() != StatusOK { + r.err <- errors.New(resp.Error()) + } else { + r.response <- resp + } + }() + return nil +} + +// Receive waits for the response. +func (r GetTransitionDurationRequest) Receive() (GetTransitionDurationResponse, error) { + if !r.sent { + return GetTransitionDurationResponse{}, ErrNotSent + } + if receiveTimeout == 0 { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetTransitionDurationResponse{}, err + } + } else { + select { + case resp := <-r.response: + return resp, nil + case err := <-r.err: + return GetTransitionDurationResponse{}, err + case <-time.After(receiveTimeout): + return GetTransitionDurationResponse{}, ErrReceiveTimeout + } + } +} + +// SendReceive sends the request then immediately waits for the response. +func (r GetTransitionDurationRequest) SendReceive(c *Client) (GetTransitionDurationResponse, error) { + if err := r.Send(c); err != nil { + return GetTransitionDurationResponse{}, err + } + return r.Receive() +} + +// GetTransitionDurationResponse : Response for GetTransitionDurationRequest. +// +// Since obs-websocket version: 4.1.0. +// +// https://github.com/Palakis/obs-websocket/blob/4.3-maintenance/docs/generated/protocol.md#gettransitionduration +type GetTransitionDurationResponse struct { + // Duration of the current transition (in milliseconds). + // Required: Yes. + TransitionDuration int `json:"transition-duration"` + _response `json:",squash"` +} diff --git a/plugin/images/actions/replay_off.png b/plugin/images/actions/replay_off.png new file mode 100644 index 0000000000000000000000000000000000000000..7fe75063a294e7d7784d5600d15067e0126d07f0 GIT binary patch literal 5145 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%0D$eB12X$ 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

Q; zt5R%#IyT7LlC6{FaIJ9fokzq4A`erQVR)Fz*`q%3VKYs#d3$yvDfoEY{1fH^(dx?| z!en-q4G!rr&fB8(*8o;DWoauCv3X^OP#>Kaadl#y zL5e4}&1^kOtRCkUD`N_Eb)YC=V5h=#?-~1jRJ}66eLa~SHs$E1L3gEaLhLip-xX;* z`cgQN46d9rZ>h~OL$E`>+~3GqN-GA=G?w*Ou(N=H6vmIez`Li5;t54mR>He&tTsFh z?cjBV6}^`aC|hxPaJX_$WzEWjk0NvMWlW9jU=xPK6+EKK6?(G3Hk$|BGI?aGH3D-Q z;%!+O%#qt#=OENOXHOtXmr-tHV_`NL_vrWWvJQuQiXWxjSBo%sBVPsQEpmd zh)s4RDx@}!aQ(w`z4I*=^{}~>Gk2QmIkb#hRk%6aWbFoaUNiP!xm9I3R4h$0H2Lk1 zX*HFnbJ8i&a#mj?BLGyG2QD;$K{fv9?-XgEp>xon)K`Q47rm-HHf;DCvh(buw%eJZ zx?ne*0O&-lb4>zV!AsoneB`2|$457-RF?kog<_jpPrPZGK2XV32{=6eR8CQyp<5I( zt6-x9b16uBY0&bIxseuhBify}3x<_hD;$W9|F69r{oni37NKKM>_^ij`%&nH4Vay_ KG^#l17X3dTe^wp< literal 0 HcmV?d00001 diff --git a/plugin/images/actions/replay_save.png b/plugin/images/actions/replay_save.png new file mode 100644 index 0000000000000000000000000000000000000000..f1109aafa7dd9694f3197a97331e790912e23b39 GIT binary patch literal 7494 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 TM^o_36EM^>(XG^Wdi=itZzj`~ literal 0 HcmV?d00001 diff --git a/plugin/images/background.png b/plugin/images/background.png new file mode 100644 index 0000000000000000000000000000000000000000..60a0169acd56439383d7688d37153c41e9689a3f GIT binary patch literal 2475 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 + + diff --git a/plugin/pi/css/sdpi.css b/plugin/pi/css/sdpi.css new file mode 100644 index 0000000..62c3336 --- /dev/null +++ b/plugin/pi/css/sdpi.css @@ -0,0 +1,1483 @@ +html { + --sdpi-bgcolor: #2D2D2D; + --sdpi-background: #3D3D3D; + --sdpi-color: #d8d8d8; + --sdpi-bordercolor: #3a3a3a; + --sdpi-buttonbordercolor: #969696; + --sdpi-borderradius: 0px; + --sdpi-width: 224px; + --sdpi-fontweight: 600; + --sdpi-letterspacing: -0.25pt; + height: 100%; + width: 100%; + overflow: hidden; + touch-action:none; +} + +html, body { + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 9pt; + background-color: var(--sdpi-bgcolor); + color: #9a9a9a; +} + +body { + height: 100%; + padding: 0; + overflow-x: hidden; + overflow-y: auto; + margin: 0; + -webkit-overflow-scrolling: touch; + -webkit-text-size-adjust: 100%; + -webkit-font-smoothing: antialiased; +} + +mark { + background-color: var(--sdpi-bgcolor); + color: var(--sdpi-color); +} + +hr, hr2 { + -webkit-margin-before: 1em; + -webkit-margin-after: 1em; + border-style: none; + background: var(--sdpi-background); + height: 1px; +} + +hr2, +.sdpi-heading { + display: flex; + flex-basis: 100%; + align-items: center; + color: inherit; + font-size: 9pt; + margin: 8px 0px; +} + +.sdpi-heading::before, +.sdpi-heading::after { + content: ""; + flex-grow: 1; + background: var(--sdpi-background); + height: 1px; + font-size: 0px; + line-height: 0px; + margin: 0px 16px; +} + +hr2 { + height: 2px; +} + +hr, hr2 { + margin-left:16px; + margin-right:16px; +} + +.sdpi-item-value, +option, +input, +select, +button { + font-size: 10pt; + font-weight: var(--sdpi-fontweight); + letter-spacing: var(--sdpi-letterspacing); +} + + + +.win .sdpi-item-value, +.win option, +.win input, +.win select, +.win button { + font-size: 11px; + font-style: normal; + letter-spacing: inherit; + font-weight: 100; +} + +.win button { + font-size: 12px; +} + +::-webkit-progress-value, +meter::-webkit-meter-optimum-value { + border-radius: 2px; + /* background: linear-gradient(#ccf, #99f 20%, #77f 45%, #77f 55%, #cdf); */ +} + +::-webkit-progress-bar, +meter::-webkit-meter-bar { + border-radius: 3px; + background: var(--sdpi-background); +} + +::-webkit-progress-bar:active, +meter::-webkit-meter-bar:active { + border-radius: 3px; + background: #222222; +} +::-webkit-progress-value:active, +meter::-webkit-meter-optimum-value:active { + background: #99f; +} + +progress, +progress.sdpi-item-value { + min-height: 5px !important; + height: 5px; + background-color: #303030; +} + +progress { + margin-top: 8px !important; + margin-bottom: 8px !important; +} + +.full progress, +progress.full { + margin-top: 3px !important; +} + +::-webkit-progress-inner-element { + background-color: transparent; +} + + +.sdpi-item[type="progress"] { + margin-top: 4px !important; + margin-bottom: 12px; + min-height: 15px; +} + +.sdpi-item-child.full:last-child { + margin-bottom: 4px; +} + +.tabs { + /** + * Setting display to flex makes this container lay + * out its children using flexbox, the exact same + * as in the above "Stepper input" example. + */ + display: flex; + + border-bottom: 1px solid #D7DBDD; +} + +.tab { + cursor: pointer; + padding: 5px 30px; + color: #16a2d7; + font-size: 9pt; + border-bottom: 2px solid transparent; +} + +.tab.is-tab-selected { + border-bottom-color: #4ebbe4; +} + +select { + -webkit-appearance: none; + -moz-appearance: none; + -o-appearance: none; + appearance: none; + background: url(caret.svg) no-repeat 97% center; +} + +label.sdpi-file-label, +input[type="button"], +input[type="submit"], +input[type="reset"], +input[type="file"], +input[type=file]::-webkit-file-upload-button, +button, +select { + color: var(--sdpi-color); + border: 1pt solid #303030; + font-size: 8pt; + background-color: var(--sdpi-background); + border-radius: var(--sdpi-borderradius); +} + +label.sdpi-file-label, +input[type="button"], +input[type="submit"], +input[type="reset"], +input[type="file"], +input[type=file]::-webkit-file-upload-button, +button { + border: 1pt solid var(--sdpi-buttonbordercolor); + border-radius: var(--sdpi-borderradius); + border-color: var(--sdpi-buttonbordercolor); + min-height: 23px !important; + height: 23px !important; + margin-right: 8px; +} + +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input[type="file"] { + border-radius: var(--sdpi-borderradius); + max-width: 220px; +} + +option { + height: 1.5em; + padding: 4px; +} + +/* SDPI */ + +.sdpi-wrapper { + overflow-x: hidden; + height: 100%; +} + +.sdpi-item { + display: flex; + flex-direction: row; + min-height: 32px; + align-items: center; + margin-top: 2px; + max-width: 344px; +} + +.sdpi-item:first-child { + margin-top:-1px; +} + +.sdpi-item:last-child { + margin-bottom: 0px; +} + +.sdpi-item > *:not(.sdpi-item-label):not(meter):not(details):not(canvas) { + min-height: 26px; + padding: 0px 4px 0px 4px; +} + +.sdpi-item > *:not(.sdpi-item-label.empty):not(meter) { + min-height: 26px; + padding: 0px 4px 0px 4px; +} + + +.sdpi-item-group { + padding: 0 !important; +} + +meter.sdpi-item-value { + margin-left: 6px; +} + +.sdpi-item[type="group"] { + display: block; + margin-top: 12px; + margin-bottom: 12px; + /* border: 1px solid white; */ + flex-direction: unset; + text-align: left; +} + +.sdpi-item[type="group"] > .sdpi-item-label, +.sdpi-item[type="group"].sdpi-item-label { + width: 96%; + text-align: left; + font-weight: 700; + margin-bottom: 4px; + padding-left: 4px; +} + +dl, +ul, +ol { + -webkit-margin-before: 0px; + -webkit-margin-after: 4px; + -webkit-padding-start: 1em; + max-height: 90px; + overflow-y: scroll; + cursor: pointer; + user-select: none; +} + +table.sdpi-item-value, +dl.sdpi-item-value, +ul.sdpi-item-value, +ol.sdpi-item-value { + -webkit-margin-before: 4px; + -webkit-margin-after: 8px; + -webkit-padding-start: 1em; + width: var(--sdpi-width); + text-align: center; +} + +table > caption { + margin: 2px; +} + +.list, +.sdpi-item[type="list"] { + align-items: baseline; +} + +.sdpi-item-label { + text-align: right; + flex: none; + width: 94px; + padding-right: 4px; + font-weight: 600; + -webkit-user-select: none; +} + +.win .sdpi-item-label, +.sdpi-item-label > small{ + font-weight: normal; +} + +.sdpi-item-label:after { + content: ": "; +} + +.sdpi-item-label.empty:after { + content: ""; +} + +.sdpi-test, +.sdpi-item-value { + flex: 1 0 0; + /* flex-grow: 1; + flex-shrink: 0; */ + margin-right: 14px; + margin-left: 4px; + justify-content: space-evenly; +} + +canvas.sdpi-item-value { + max-width: 144px; + max-height: 144px; + width: 144px; + height: 144px; + margin: 0 auto; + cursor: pointer; +} + +input.sdpi-item-value { + margin-left: 5px; +} + +.sdpi-item-value button, +button.sdpi-item-value { + margin-left: 6px; + margin-right: 14px; +} + +.sdpi-item-value.range { + margin-left: 0px; +} + +table, +dl.sdpi-item-value, +ul.sdpi-item-value, +ol.sdpi-item-value, +.sdpi-item-value > dl, +.sdpi-item-value > ul, +.sdpi-item-value > ol +{ + list-style-type: none; + list-style-position: outside; + margin-left: -4px; + margin-right: -4px; + padding: 4px; + border: 1px solid var(--sdpi-bordercolor); +} + +dl.sdpi-item-value, +ul.sdpi-item-value, +ol.sdpi-item-value, +.sdpi-item-value > ol { + list-style-type: none; + list-style-position: inside; + margin-left: 5px; + margin-right: 12px; + padding: 4px !important; +} + +ol.sdpi-item-value, +.sdpi-item-value > ol[listtype="none"] { + list-style-type: none; +} +ol.sdpi-item-value[type="decimal"], +.sdpi-item-value > ol[type="decimal"] { + list-style-type: decimal; +} + +ol.sdpi-item-value[type="decimal-leading-zero"], +.sdpi-item-value > ol[type="decimal-leading-zero"] { + list-style-type: decimal-leading-zero; +} + +ol.sdpi-item-value[type="lower-alpha"], +.sdpi-item-value > ol[type="lower-alpha"] { + list-style-type: lower-alpha; +} + +ol.sdpi-item-value[type="upper-alpha"], +.sdpi-item-value > ol[type="upper-alpha"] { + list-style-type: upper-alpha; +} + +ol.sdpi-item-value[type="upper-roman"], +.sdpi-item-value > ol[type="upper-roman"] { + list-style-type: upper-roman; +} + +ol.sdpi-item-value[type="lower-roman"], +.sdpi-item-value > ol[type="lower-roman"] { + list-style-type: upper-roman; +} + +tr:nth-child(even), +.sdpi-item-value > ul > li:nth-child(even), +.sdpi-item-value > ol > li:nth-child(even), +li:nth-child(even) { + background-color: rgba(0,0,0,.2) +} + +td:hover, +.sdpi-item-value > ul > li:hover:nth-child(even), +.sdpi-item-value > ol > li:hover:nth-child(even), +li:hover:nth-child(even), +li:hover { + background-color: rgba(255,255,255,.1); +} + +td.selected, +td.selected:hover, +li.selected:hover, +li.selected { + color: white; + background-color: #77f; +} + +tr { + border: 1px solid var(--sdpi-bordercolor); +} + +td { + border-right: 1px solid var(--sdpi-bordercolor); + -webkit-user-select: none; +} + +tr:last-child, +td:last-child { + border: none; +} + +.sdpi-item-value.select, +.sdpi-item-value > select { + margin-right: 13px; + margin-left: 4px; +} + +.sdpi-item-child, +.sdpi-item-group > .sdpi-item > input[type="color"] { + margin-top: 0.4em; + margin-right: 4px; +} + +.full, +.full *, +.sdpi-item-value.full, +.sdpi-item-child > full > *, +.sdpi-item-child.full, +.sdpi-item-child.full > *, +.full > .sdpi-item-child, +.full > .sdpi-item-child > *{ + display: flex; + flex: 1 1 0; + margin-bottom: 4px; + margin-left: 0px; + width: 100%; + + justify-content: space-evenly; +} + +.sdpi-item-group > .sdpi-item > input[type="color"] { + margin-top: 0px; +} + +::-webkit-calendar-picker-indicator:focus, +input[type=file]::-webkit-file-upload-button:focus, +button:focus, +textarea:focus, +input:focus, +select:focus, +option:focus, +details:focus, +summary:focus, +.custom-select select { + outline: none; +} + +summary { + cursor: default; + -webkit-user-select: none; +} + +.pointer, +summary .pointer { + cursor: pointer; +} + +details.message { + padding: 4px 18px 4px 12px; +} + +details.message summary { + font-size: 10pt; + font-weight: 600; + min-height: 18px; +} + +details.message:first-child { + margin-top: 4px; + margin-left: 0; + padding-left: 102px; +} + +details.message h1 { + text-align: left; +} + +.message > summary::-webkit-details-marker { + display: none; +} + +.info20, +.question, +.caution, +.info { + background-repeat: no-repeat; + background-position: 72px center; +} + +.info20 { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,20 C4.4771525,20 0,15.5228475 0,10 C0,4.4771525 4.4771525,0 10,0 C15.5228475,0 20,4.4771525 20,10 C20,15.5228475 15.5228475,20 10,20 Z M10,8 C8.8954305,8 8,8.84275812 8,9.88235294 L8,16.1176471 C8,17.1572419 8.8954305,18 10,18 C11.1045695,18 12,17.1572419 12,16.1176471 L12,9.88235294 C12,8.84275812 11.1045695,8 10,8 Z M10,3 C8.8954305,3 8,3.88165465 8,4.96923077 L8,5.03076923 C8,6.11834535 8.8954305,7 10,7 C11.1045695,7 12,6.11834535 12,5.03076923 L12,4.96923077 C12,3.88165465 11.1045695,3 10,3 Z'/%3E%3C/svg%3E%0A"); +} + +.info { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M10,8 C9.44771525,8 9,8.42137906 9,8.94117647 L9,14.0588235 C9,14.5786209 9.44771525,15 10,15 C10.5522847,15 11,14.5786209 11,14.0588235 L11,8.94117647 C11,8.42137906 10.5522847,8 10,8 Z M10,5 C9.44771525,5 9,5.44082732 9,5.98461538 L9,6.01538462 C9,6.55917268 9.44771525,7 10,7 C10.5522847,7 11,6.55917268 11,6.01538462 L11,5.98461538 C11,5.44082732 10.5522847,5 10,5 Z'/%3E%3C/svg%3E%0A"); +} + +.info2 { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 15 15'%3E%3Cpath fill='%23999' d='M7.5,15 C3.35786438,15 0,11.6421356 0,7.5 C0,3.35786438 3.35786438,0 7.5,0 C11.6421356,0 15,3.35786438 15,7.5 C15,11.6421356 11.6421356,15 7.5,15 Z M7.5,2 C6.67157287,2 6,2.66124098 6,3.47692307 L6,3.52307693 C6,4.33875902 6.67157287,5 7.5,5 C8.32842705,5 9,4.33875902 9,3.52307693 L9,3.47692307 C9,2.66124098 8.32842705,2 7.5,2 Z M5,6 L5,7.02155172 L6,7 L6,12 L5,12.0076778 L5,13 L10,13 L10,12 L9,12.0076778 L9,6 L5,6 Z'/%3E%3C/svg%3E%0A"); +} + +.sdpi-more-info { + background-image: linear-gradient(to right, #00000000 0%,#00000040 80%), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpolygon fill='%23999' points='4 7 8 7 8 5 12 8 8 11 8 9 4 9'/%3E%3C/svg%3E%0A"); +} +.caution { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' fill-rule='evenodd' d='M9.03952676,0.746646542 C9.57068894,-0.245797319 10.4285735,-0.25196227 10.9630352,0.746646542 L19.7705903,17.2030214 C20.3017525,18.1954653 19.8777595,19 18.8371387,19 L1.16542323,19 C0.118729947,19 -0.302490098,18.2016302 0.231971607,17.2030214 L9.03952676,0.746646542 Z M10,2.25584053 L1.9601405,17.3478261 L18.04099,17.3478261 L10,2.25584053 Z M10,5.9375 C10.531043,5.9375 10.9615385,6.37373537 10.9615385,6.91185897 L10.9615385,11.6923077 C10.9615385,12.2304313 10.531043,12.6666667 10,12.6666667 C9.46895697,12.6666667 9.03846154,12.2304313 9.03846154,11.6923077 L9.03846154,6.91185897 C9.03846154,6.37373537 9.46895697,5.9375 10,5.9375 Z M10,13.4583333 C10.6372516,13.4583333 11.1538462,13.9818158 11.1538462,14.6275641 L11.1538462,14.6641026 C11.1538462,15.3098509 10.6372516,15.8333333 10,15.8333333 C9.36274837,15.8333333 8.84615385,15.3098509 8.84615385,14.6641026 L8.84615385,14.6275641 C8.84615385,13.9818158 9.36274837,13.4583333 10,13.4583333 Z'/%3E%3C/svg%3E%0A"); +} + +.question { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath fill='%23999' d='M10,18 C5.581722,18 2,14.418278 2,10 C2,5.581722 5.581722,2 10,2 C14.418278,2 18,5.581722 18,10 C18,14.418278 14.418278,18 10,18 Z M6.77783203,7.65332031 C6.77783203,7.84798274 6.85929281,8.02888914 7.0222168,8.19604492 C7.18514079,8.36320071 7.38508996,8.44677734 7.62207031,8.44677734 C8.02409055,8.44677734 8.29703704,8.20768468 8.44091797,7.72949219 C8.59326248,7.27245865 8.77945854,6.92651485 8.99951172,6.69165039 C9.2195649,6.45678594 9.56233491,6.33935547 10.027832,6.33935547 C10.4256205,6.33935547 10.7006836,6.37695313 11.0021973,6.68847656 C11.652832,7.53271484 10.942627,8.472229 10.3750916,9.1321106 C9.80755615,9.79199219 8.29492188,11.9897461 10.027832,12.1347656 C10.4498423,12.1700818 10.7027991,11.9147157 10.7832031,11.4746094 C11.0021973,9.59857178 13.1254883,8.82415771 13.1254883,7.53271484 C13.1254883,7.07568131 12.9974785,6.65250846 12.7414551,6.26318359 C12.4854317,5.87385873 12.1225609,5.56600048 11.652832,5.33959961 C11.1831031,5.11319874 10.6414419,5 10.027832,5 C9.36767248,5 8.79004154,5.13541531 8.29492187,5.40625 C7.79980221,5.67708469 7.42317837,6.01879677 7.16503906,6.43139648 C6.90689975,6.8439962 6.77783203,7.25130007 6.77783203,7.65332031 Z M10.0099668,15 C10.2713191,15 10.5016601,14.9108147 10.7009967,14.7324415 C10.9003332,14.5540682 11,14.3088087 11,13.9966555 C11,13.7157177 10.9047629,13.4793767 10.7142857,13.2876254 C10.5238086,13.0958742 10.2890379,13 10.0099668,13 C9.72646591,13 9.48726565,13.0958742 9.2923588,13.2876254 C9.09745196,13.4793767 9,13.7157177 9,13.9966555 C9,14.313268 9.10077419,14.5596424 9.30232558,14.735786 C9.50387698,14.9119295 9.73975502,15 10.0099668,15 Z'/%3E%3C/svg%3E%0A"); +} + + +.sdpi-more-info { + position: fixed; + left: 0px; + right: 0px; + bottom: 0px; + min-height:16px; + padding-right: 16px; + text-align: right; + -webkit-touch-callout: none; + cursor: pointer; + user-select: none; + background-position: right center; + background-repeat: no-repeat; + border-radius: var(--sdpi-borderradius); + text-decoration: none; + color: var(--sdpi-color); +} + +.sdpi-more-info-button { + display: flex; + align-self: right; + margin-left: auto; + position: fixed; + right: 17px; + bottom: 0px; + user-select: none; +} + +details a { + background-position: right !important; + min-height: 24px; + display: inline-block; + line-height: 24px; + padding-right: 28px; +} + + +input:not([type="range"]), +textarea { + -webkit-appearance: none; + background: var(--sdpi-background); + color: var(--sdpi-color); + font-weight: normal; + font-size: 9pt; + border: none; + margin-top: 2px; + margin-bottom: 2px; + min-width: 219px; +} + +textarea + label { + display: flex; + justify-content: flex-end +} +input[type="radio"], +input[type="checkbox"] { + display: none; +} +input[type="radio"] + label, +input[type="checkbox"] + label { + font-size: 9pt; + color: var(--sdpi-color); + font-weight: normal; + margin-right: 8px; + -webkit-user-select: none; +} + +input[type="radio"] + label:after, +input[type="checkbox"] + label:after { + content: " " !important; +} + +.sdpi-item[type="radio"] > .sdpi-item-value, +.sdpi-item[type="checkbox"] > .sdpi-item-value { + padding-top: 2px; +} + +.sdpi-item[type="checkbox"] > .sdpi-item-value > * { + margin-top: 4px; +} + +.sdpi-item[type="checkbox"] .sdpi-item-child, +.sdpi-item[type="radio"] .sdpi-item-child { + display: inline-block; +} + +.sdpi-item[type="range"] .sdpi-item-value, +.sdpi-item[type="meter"] .sdpi-item-child, +.sdpi-item[type="progress"] .sdpi-item-child { + display: flex; +} + +.sdpi-item[type="range"] .sdpi-item-value { + min-height: 26px; +} + +.sdpi-item[type="range"] .sdpi-item-value span, +.sdpi-item[type="meter"] .sdpi-item-child span, +.sdpi-item[type="progress"] .sdpi-item-child span { + margin-top: -2px; + min-width: 8px; + text-align: right; + user-select: none; + cursor: pointer; +} + +.sdpi-item[type="range"] .sdpi-item-value span { + margin-top: 7px; + text-align: right; +} + +span + input[type="range"] { + display: flex; + max-width: 168px; + +} + +.sdpi-item[type="range"] .sdpi-item-value span:first-child, +.sdpi-item[type="meter"] .sdpi-item-child span:first-child, +.sdpi-item[type="progress"] .sdpi-item-child span:first-child { + margin-right: 4px; +} + +.sdpi-item[type="range"] .sdpi-item-value span:last-child, +.sdpi-item[type="meter"] .sdpi-item-child span:last-child, +.sdpi-item[type="progress"] .sdpi-item-child span:last-child { + margin-left: 4px; +} + +.reverse { + transform: rotate(180deg); +} + +.sdpi-item[type="meter"] .sdpi-item-child meter + span:last-child { + margin-left: -10px; +} + +.sdpi-item[type="progress"] .sdpi-item-child meter + span:last-child { + margin-left: -14px; +} + +.sdpi-item[type="radio"] > .sdpi-item-value > * { + margin-top: 2px; +} + +details { + padding: 8px 18px 8px 12px; + min-width: 86px; +} + +details > h4 { + border-bottom: 1px solid var(--sdpi-bordercolor); +} + +legend { + display: none; +} +.sdpi-item-value > textarea { + padding: 0px; + width: 219px; + margin-left: 1px; + margin-top: 3px; + padding: 4px; +} + +input[type="radio"] + label span, +input[type="checkbox"] + label span { + display: inline-block; + width: 16px; + height: 16px; + margin: 2px 4px 2px 0; + border-radius: 3px; + vertical-align: middle; + background: var(--sdpi-background); + cursor: pointer; + border: 1px solid rgb(0,0,0,.2); +} + +input[type="radio"] + label span { + border-radius: 100%; +} + +input[type="radio"]:checked + label span, +input[type="checkbox"]:checked + label span { + background-color: #77f; + background-image: url(check.svg); + background-repeat: no-repeat; + background-position: center center; + border: 1px solid rgb(0,0,0,.4); +} + +input[type="radio"]:active:checked + label span, +input[type="radio"]:active + label span, +input[type="checkbox"]:active:checked + label span, +input[type="checkbox"]:active + label span { + background-color: #303030; +} + +input[type="radio"]:checked + label span { + background-image: url(rcheck.svg); +} + +/* +input[type="radio"] + label span { + background: url(buttons.png) -38px top no-repeat; +} + +input[type="radio"]:checked + label span { + background: url(buttons.png) -57px top no-repeat; +} +*/ + +input[type="range"] { + width: var(--sdpi-width); + height: 30px; + overflow: hidden; + cursor: pointer; + background: transparent !important; +} + +.sdpi-item > input[type="range"] { + margin-left: 2px; + max-width: var(--sdpi-width); + width: var(--sdpi-width); + padding: 0px; + margin-top: 4px; +} + +/* +input[type="range"], +input[type="range"]::-webkit-slider-runnable-track, +input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; +} +*/ + +input[type="range"]::-webkit-slider-runnable-track { + height: 5px; + background: #979797; + border-radius: 3px; + padding:0px !important; + border: 1px solid var(--sdpi-background); +} + +input[type="range"]::-webkit-slider-thumb { + position: relative; + -webkit-appearance: none; + background-color: var(--sdpi-color); + width: 12px; + height: 12px; + border-radius: 20px; + margin-top: -5px; + border: none; +} +input[type="range" i]{ + margin: 0; +} + +input[type="range"]::-webkit-slider-thumb::before { + position: absolute; + content: ""; + height: 5px; /* equal to height of runnable track or 1 less */ + width: 500px; /* make this bigger than the widest range input element */ + left: -502px; /* this should be -2px - width */ + top: 8px; /* don't change this */ + background: #77f; +} + +input[type="color"] { + min-width: 32px; + min-height: 32px; + width: 32px; + height: 32px; + padding: 0; + background-color: var(--sdpi-bgcolor); + flex: none; +} + +::-webkit-color-swatch { + min-width: 24px; +} + +textarea { + height: 3em; + word-break: break-word; + line-height: 1.5em; +} + +.textarea { + padding: 0px !important; +} + +textarea { + width: 219px; /*98%;*/ + height: 96%; + min-height: 6em; + resize: none; + border-radius: var(--sdpi-borderradius); +} + +/* CAROUSEL */ + +.sdpi-item[type="carousel"]{ + +} + +.sdpi-item.card-carousel-wrapper, +.sdpi-item > .card-carousel-wrapper { + padding: 0; +} + + +.card-carousel-wrapper { + display: flex; + align-items: center; + justify-content: center; + margin: 12px auto; + color: #666a73; +} + +.card-carousel { + display: flex; + justify-content: center; + width: 278px; +} +.card-carousel--overflow-container { + overflow: hidden; +} +.card-carousel--nav__left, +.card-carousel--nav__right { + /* display: inline-block; */ + width: 12px; + height: 12px; + border-top: 2px solid #42b883; + border-right: 2px solid #42b883; + cursor: pointer; + margin: 0 4px; + transition: transform 150ms linear; +} +.card-carousel--nav__left[disabled], +.card-carousel--nav__right[disabled] { + opacity: 0.2; + border-color: black; +} +.card-carousel--nav__left { + transform: rotate(-135deg); +} +.card-carousel--nav__left:active { + transform: rotate(-135deg) scale(0.85); +} +.card-carousel--nav__right { + transform: rotate(45deg); +} +.card-carousel--nav__right:active { + transform: rotate(45deg) scale(0.85); +} +.card-carousel-cards { + display: flex; + transition: transform 150ms ease-out; + transform: translatex(0px); +} +.card-carousel-cards .card-carousel--card { + margin: 0 5px; + cursor: pointer; + /* box-shadow: 0 4px 15px 0 rgba(40, 44, 53, 0.06), 0 2px 2px 0 rgba(40, 44, 53, 0.08); */ + background-color: #fff; + border-radius: 4px; + z-index: 3; +} +.xxcard-carousel-cards .card-carousel--card:first-child { + margin-left: 0; +} +.xxcard-carousel-cards .card-carousel--card:last-child { + margin-right: 0; +} +.card-carousel-cards .card-carousel--card img { + vertical-align: bottom; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + transition: opacity 150ms linear; + width: 60px; +} +.card-carousel-cards .card-carousel--card img:hover { + opacity: 0.5; +} +.card-carousel-cards .card-carousel--card--footer { + border-top: 0; + max-width: 80px; + overflow: hidden; + display: flex; + height: 100%; + flex-direction: column; +} +.card-carousel-cards .card-carousel--card--footer p { + padding: 3px 0; + margin: 0; + margin-bottom: 2px; + font-size: 15px; + font-weight: 500; + color: #2c3e50; +} +.card-carousel-cards .card-carousel--card--footer p:nth-of-type(2) { + font-size: 12px; + font-weight: 300; + padding: 6px; + color: #666a73; +} + + +h1 { + font-size: 1.3em; + font-weight: 500; + text-align: center; + margin-bottom: 12px; +} + +::-webkit-datetime-edit { + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + background: url(elg_calendar_inv.svg) no-repeat left center; + padding-right: 1em; + padding-left: 25px; + background-position: 4px 0px; +} +::-webkit-datetime-edit-fields-wrapper { + +} +::-webkit-datetime-edit-text { padding: 0 0.3em; } +::-webkit-datetime-edit-month-field { } +::-webkit-datetime-edit-day-field {} +::-webkit-datetime-edit-year-field {} +::-webkit-inner-spin-button { + + /* display: none; */ +} +::-webkit-calendar-picker-indicator { + background: transparent; + font-size: 17px; +} + +::-webkit-calendar-picker-indicator:focus { + background-color: rgba(0,0,0,0.2); +} + +input[type="date"] { + -webkit-align-items: center; + display: -webkit-inline-flex; + font-family: monospace; + overflow: hidden; + padding: 0; + -webkit-padding-start: 1px; +} + +input::-webkit-datetime-edit { + -webkit-flex: 1; + -webkit-user-modify: read-only !important; + display: inline-block; + min-width: 0; + overflow: hidden; +} + +/* +input::-webkit-datetime-edit-fields-wrapper { + -webkit-user-modify: read-only !important; + display: inline-block; + padding: 1px 0; + white-space: pre; + +} +*/ + +/* +input[type="date"] { + background-color: red; + outline: none; +} + +input[type="date"]::-webkit-clear-button { + font-size: 18px; + height: 30px; + position: relative; +} + +input[type="date"]::-webkit-inner-spin-button { + height: 28px; +} + +input[type="date"]::-webkit-calendar-picker-indicator { + font-size: 15px; +} */ + +input[type="file"] { + opacity: 0; + display: none; +} + +.sdpi-item > input[type="file"] { + opacity: 1; + display: flex; +} + +input[type="file"] + span { + display: flex; + flex: 0 1 auto; + background-color: #0000ff50; +} + +label.sdpi-file-label { + cursor: pointer; + user-select: none; + display: inline-block; + min-height: 21px !important; + height: 21px !important; + line-height: 20px; + padding: 0px 4px; + margin: auto; + margin-right: 0px; + float:right; +} + +.sdpi-file-label > label:active, +.sdpi-file-label.file:active, +label.sdpi-file-label:active, +label.sdpi-file-info:active, +input[type="file"]::-webkit-file-upload-button:active, +button:active { + background-color: var(--sdpi-color); + color:#303030; +} + +input:required:invalid, input:focus:invalid { + background: var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPgogICAgPHBhdGggZmlsbD0iI0Q4RDhEOCIgZD0iTTQuNSwwIEM2Ljk4NTI4MTM3LC00LjU2NTM4NzgyZS0xNiA5LDIuMDE0NzE4NjMgOSw0LjUgQzksNi45ODUyODEzNyA2Ljk4NTI4MTM3LDkgNC41LDkgQzIuMDE0NzE4NjMsOSAzLjA0MzU5MTg4ZS0xNiw2Ljk4NTI4MTM3IDAsNC41IEMtMy4wNDM1OTE4OGUtMTYsMi4wMTQ3MTg2MyAyLjAxNDcxODYzLDQuNTY1Mzg3ODJlLTE2IDQuNSwwIFogTTQsMSBMNCw2IEw1LDYgTDUsMSBMNCwxIFogTTQuNSw4IEM0Ljc3NjE0MjM3LDggNSw3Ljc3NjE0MjM3IDUsNy41IEM1LDcuMjIzODU3NjMgNC43NzYxNDIzNyw3IDQuNSw3IEM0LjIyMzg1NzYzLDcgNCw3LjIyMzg1NzYzIDQsNy41IEM0LDcuNzc2MTQyMzcgNC4yMjM4NTc2Myw4IDQuNSw4IFoiLz4KICA8L3N2Zz4) no-repeat 98% center; +} + +input:required:valid { + background: var(--sdpi-background) url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5IiBoZWlnaHQ9IjkiIHZpZXdCb3g9IjAgMCA5IDkiPjxwb2x5Z29uIGZpbGw9IiNEOEQ4RDgiIHBvaW50cz0iNS4yIDEgNi4yIDEgNi4yIDcgMy4yIDcgMy4yIDYgNS4yIDYiIHRyYW5zZm9ybT0icm90YXRlKDQwIDQuNjc3IDQpIi8+PC9zdmc+) no-repeat 98% center; +} + +.tooltip, +:tooltip, +:title { + color: yellow; +} + +[title]:hover { + display: flex; + align-items: center; + justify-content: center; +} + +[title]:hover::after { + content: ''; + position: absolute; + bottom: -1000px; + left: 8px; + display: none; + color: #fff; + border: 8px solid transparent; + border-bottom: 8px solid #000; +} +[title]:hover::before { + content: attr(title); + display: flex; + justify-content: center; + align-self: center; + padding: 6px 12px; + border-radius: 5px; + background: rgba(0,0,0,0.8); + color: var(--sdpi-color); + font-size: 9pt; + font-family: sans-serif; + opacity: 1; + position: absolute; + height: auto; + /* width: 50%; + left: 35%; */ + text-align: center; + bottom: 2px; + z-index: 100; + box-shadow: 0px 3px 6px rgba(0, 0, 0, .5); + /* box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); */ +} + +.sdpi-item-group.file { + width: 232px; + display: flex; + align-items: center; +} + +.sdpi-file-info { + overflow-wrap: break-word; + word-wrap: break-word; + hyphens: auto; + + min-width: 132px; + max-width: 144px; + max-height: 32px; + margin-top: 0px; + margin-left: 5px; + display: inline-block; + overflow: hidden; + padding: 6px 4px; + background-color: var(--sdpi-background); +} + + +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); +} + +::-webkit-scrollbar-thumb { + background-color: #999999; + outline: 1px solid slategrey; + border-radius: 8px; +} + +a { + color: #7397d2; +} + +.testcontainer { + display: flex; + background-color: #0000ff20; + max-width: 400px; + height: 200px; + align-content: space-evenly; +} + +input[type=range] { + -webkit-appearance: none; + /* background-color: green; */ + height:6px; + margin-top: 12px; + z-index: 0; + overflow: visible; +} + +/* +input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + background-color: var(--sdpi-color); + width: 12px; + height: 12px; + border-radius: 20px; + margin-top: -6px; + border: none; +} */ + +:-webkit-slider-thumb { + -webkit-appearance: none; + background-color: var(--sdpi-color); + width: 16px; + height: 16px; + border-radius: 20px; + margin-top: -6px; + border: 1px solid #999999; +} + +.sdpi-item[type="range"] .sdpi-item-group { + display: flex; + flex-direction: column; +} + +.xxsdpi-item[type="range"] .sdpi-item-group input { + max-width: 204px; +} + +.sdpi-item[type="range"] .sdpi-item-group span { + margin-left: 0px !important; +} + +.sdpi-item[type="range"] .sdpi-item-group > .sdpi-item-child { + display: flex; + flex-direction: row; +} + +.rangeLabel { + position:absolute; + font-weight:normal; + margin-top:22px; +} + +:disabled { + color: #993333; +} + +select, +select option { + color: var(--sdpi-color); +} + +select.disabled, +select option:disabled { + color: #fd9494; + font-style: italic; +} + +.runningAppsContainer { + display: none; +} + +/* debug +div { + background-color: rgba(64,128,255,0.2); +} +*/ + +.one-line { + min-height: 1.5em; +} + +.two-lines { + min-height: 3em; +} + +.three-lines { + min-height: 4.5em; +} + +.four-lines { + min-height: 6em; +} + +.min80 > .sdpi-item-child { + min-width: 80px; +} + +.min100 > .sdpi-item-child { + min-width: 100px; +} + +.min120 > .sdpi-item-child { + min-width: 120px; +} + +.min140 > .sdpi-item-child { + min-width: 140px; +} + +.min160 > .sdpi-item-child { + min-width: 160px; +} + +.min200 > .sdpi-item-child { + min-width: 200px; +} + +.max40 { + flex-basis: 40%; + flex-grow: 0; +} + +.max30 { + flex-basis: 30%; + flex-grow: 0; +} + +.max20 { + flex-basis: 20%; + flex-grow: 0; +} + +.up20 { + margin-top: -20px; +} + +.alignCenter { + align-items: center; +} + +.alignTop { + align-items: flex-start; +} + +.alignBaseline { + align-items: baseline; +} + +.noMargins, +.noMargins *, +.noInnerMargins * { + margin: 0; + padding: 0; +} + +.hidden { + display: none; +} + +.icon-brighter, +.icon-darker, +.icon-warmer, +.icon-cooler { + margin-top: 5px !important; + min-width: 20px; + width: 20px; + background-repeat: no-repeat; +} + +.icon-brighter { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Ccircle cx='10' cy='10' r='4'/%3E%3Cpath d='M14.8532861,7.77530426 C14.7173255,7.4682615 14.5540843,7.17599221 14.3666368,6.90157083 L16.6782032,5.5669873 L17.1782032,6.4330127 L14.8532861,7.77530426 Z M10.5,4.5414007 C10.2777625,4.51407201 10.051423,4.5 9.82179677,4.5 C9.71377555,4.5 9.60648167,4.50311409 9.5,4.50925739 L9.5,2 L10.5,2 L10.5,4.5414007 Z M5.38028092,6.75545367 C5.18389364,7.02383457 5.01124349,7.31068015 4.86542112,7.61289977 L2.82179677,6.4330127 L3.32179677,5.5669873 L5.38028092,6.75545367 Z M4.86542112,12.3871002 C5.01124349,12.6893198 5.18389364,12.9761654 5.38028092,13.2445463 L3.32179677,14.4330127 L2.82179677,13.5669873 L4.86542112,12.3871002 Z M9.5,15.4907426 C9.60648167,15.4968859 9.71377555,15.5 9.82179677,15.5 C10.051423,15.5 10.2777625,15.485928 10.5,15.4585993 L10.5,18 L9.5,18 L9.5,15.4907426 Z M14.3666368,13.0984292 C14.5540843,12.8240078 14.7173255,12.5317385 14.8532861,12.2246957 L17.1782032,13.5669873 L16.6782032,14.4330127 L14.3666368,13.0984292 Z'/%3E%3C/g%3E%3C/svg%3E"); +} +.icon-darker { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10 14C7.790861 14 6 12.209139 6 10 6 7.790861 7.790861 6 10 6 12.209139 6 14 7.790861 14 10 14 12.209139 12.209139 14 10 14zM10 13C11.6568542 13 13 11.6568542 13 10 13 8.34314575 11.6568542 7 10 7 8.34314575 7 7 8.34314575 7 10 7 11.6568542 8.34314575 13 10 13zM14.8532861 7.77530426C14.7173255 7.4682615 14.5540843 7.17599221 14.3666368 6.90157083L16.6782032 5.5669873 17.1782032 6.4330127 14.8532861 7.77530426zM10.5 4.5414007C10.2777625 4.51407201 10.051423 4.5 9.82179677 4.5 9.71377555 4.5 9.60648167 4.50311409 9.5 4.50925739L9.5 2 10.5 2 10.5 4.5414007zM5.38028092 6.75545367C5.18389364 7.02383457 5.01124349 7.31068015 4.86542112 7.61289977L2.82179677 6.4330127 3.32179677 5.5669873 5.38028092 6.75545367zM4.86542112 12.3871002C5.01124349 12.6893198 5.18389364 12.9761654 5.38028092 13.2445463L3.32179677 14.4330127 2.82179677 13.5669873 4.86542112 12.3871002zM9.5 15.4907426C9.60648167 15.4968859 9.71377555 15.5 9.82179677 15.5 10.051423 15.5 10.2777625 15.485928 10.5 15.4585993L10.5 18 9.5 18 9.5 15.4907426zM14.3666368 13.0984292C14.5540843 12.8240078 14.7173255 12.5317385 14.8532861 12.2246957L17.1782032 13.5669873 16.6782032 14.4330127 14.3666368 13.0984292z'/%3E%3C/g%3E%3C/svg%3E"); +} +.icon-warmer { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M12.3247275 11.4890349C12.0406216 11.0007637 11.6761954 10.5649925 11.2495475 10.1998198 11.0890394 9.83238991 11 9.42659309 11 9 11 7.34314575 12.3431458 6 14 6 15.6568542 6 17 7.34314575 17 9 17 10.6568542 15.6568542 12 14 12 13.3795687 12 12.8031265 11.8116603 12.3247275 11.4890349zM17.6232392 11.6692284C17.8205899 11.4017892 17.9890383 11.1117186 18.123974 10.8036272L20.3121778 12.0669873 19.8121778 12.9330127 17.6232392 11.6692284zM18.123974 7.19637279C17.9890383 6.88828142 17.8205899 6.5982108 17.6232392 6.33077158L19.8121778 5.0669873 20.3121778 5.9330127 18.123974 7.19637279zM14.5 4.52746439C14.3358331 4.50931666 14.1690045 4.5 14 4.5 13.8309955 4.5 13.6641669 4.50931666 13.5 4.52746439L13.5 2 14.5 2 14.5 4.52746439zM13.5 13.4725356C13.6641669 13.4906833 13.8309955 13.5 14 13.5 14.1690045 13.5 14.3358331 13.4906833 14.5 13.4725356L14.5 16 13.5 16 13.5 13.4725356zM14 11C15.1045695 11 16 10.1045695 16 9 16 7.8954305 15.1045695 7 14 7 12.8954305 7 12 7.8954305 12 9 12 10.1045695 12.8954305 11 14 11zM9.5 11C10.6651924 11.4118364 11.5 12.5 11.5 14 11.5 16 10 17.5 8 17.5 6 17.5 4.5 16 4.5 14 4.5 12.6937812 5 11.5 6.5 11L6.5 7 9.5 7 9.5 11z'/%3E%3Cpath d='M12,14 C12,16.209139 10.209139,18 8,18 C5.790861,18 4,16.209139 4,14 C4,12.5194353 4.80439726,11.2267476 6,10.5351288 L6,4 C6,2.8954305 6.8954305,2 8,2 C9.1045695,2 10,2.8954305 10,4 L10,10.5351288 C11.1956027,11.2267476 12,12.5194353 12,14 Z M11,14 C11,12.6937812 10.1651924,11.5825421 9,11.1707057 L9,4 C9,3.44771525 8.55228475,3 8,3 C7.44771525,3 7,3.44771525 7,4 L7,11.1707057 C5.83480763,11.5825421 5,12.6937812 5,14 C5,15.6568542 6.34314575,17 8,17 C9.65685425,17 11,15.6568542 11,14 Z'/%3E%3C/g%3E%3C/svg%3E"); +} + +.icon-cooler { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cg fill='%23999' fill-rule='evenodd'%3E%3Cpath d='M10.4004569 11.6239517C10.0554735 10.9863849 9.57597206 10.4322632 9 9.99963381L9 9.7450467 9.53471338 9.7450467 10.8155381 8.46422201C10.7766941 8.39376637 10.7419749 8.32071759 10.7117062 8.2454012L9 8.2454012 9 6.96057868 10.6417702 6.96057868C10.6677696 6.86753378 10.7003289 6.77722682 10.7389179 6.69018783L9.44918707 5.40045694 9 5.40045694 9 4.34532219 9.32816127 4.34532219 9.34532219 2.91912025 10.4004569 2.91912025 10.4004569 4.53471338 11.6098599 5.74411634C11.7208059 5.68343597 11.8381332 5.63296451 11.9605787 5.59396526L11.9605787 3.8884898 10.8181818 2.74609294 11.5642748 2 12.5727518 3.00847706 13.5812289 2 14.3273218 2.74609294 13.2454012 3.82801356 13.2454012 5.61756719C13.3449693 5.65339299 13.4408747 5.69689391 13.5324038 5.74735625L14.7450467 4.53471338 14.7450467 2.91912025 15.8001815 2.91912025 15.8001815 4.34532219 17.2263834 4.34532219 17.2263834 5.40045694 15.6963166 5.40045694 14.4002441 6.69652946C14.437611 6.78161093 14.4692249 6.86979146 14.4945934 6.96057868L16.2570138 6.96057868 17.3994107 5.81818182 18.1455036 6.56427476 17.1370266 7.57275182 18.1455036 8.58122888 17.3994107 9.32732182 16.3174901 8.2454012 14.4246574 8.2454012C14.3952328 8.31861737 14.3616024 8.38969062 14.3240655 8.45832192L15.6107903 9.7450467 17.2263834 9.7450467 17.2263834 10.8001815 15.8001815 10.8001815 15.8001815 12.2263834 14.7450467 12.2263834 14.7450467 10.6963166 13.377994 9.32926387C13.3345872 9.34850842 13.2903677 9.36625331 13.2454012 9.38243281L13.2454012 11.3174901 14.3273218 12.3994107 13.5812289 13.1455036 12.5848864 12.1491612 11.5642748 13.1455036 10.8181818 12.3994107 11.9605787 11.2570138 11.9605787 9.40603474C11.8936938 9.38473169 11.828336 9.36000556 11.7647113 9.33206224L10.4004569 10.6963166 10.4004569 11.6239517zM12.75 8.5C13.3022847 8.5 13.75 8.05228475 13.75 7.5 13.75 6.94771525 13.3022847 6.5 12.75 6.5 12.1977153 6.5 11.75 6.94771525 11.75 7.5 11.75 8.05228475 12.1977153 8.5 12.75 8.5zM9.5 14C8.5 16.3333333 7.33333333 17.5 6 17.5 4.66666667 17.5 3.5 16.3333333 2.5 14L9.5 14z'/%3E%3Cpath d='M10,14 C10,16.209139 8.209139,18 6,18 C3.790861,18 2,16.209139 2,14 C2,12.5194353 2.80439726,11.2267476 4,10.5351288 L4,4 C4,2.8954305 4.8954305,2 6,2 C7.1045695,2 8,2.8954305 8,4 L8,10.5351288 C9.19560274,11.2267476 10,12.5194353 10,14 Z M9,14 C9,12.6937812 8.16519237,11.5825421 7,11.1707057 L7,4 C7,3.44771525 6.55228475,3 6,3 C5.44771525,3 5,3.44771525 5,4 L5,11.1707057 C3.83480763,11.5825421 3,12.6937812 3,14 C3,15.6568542 4.34314575,17 6,17 C7.65685425,17 9,15.6568542 9,14 Z'/%3E%3C/g%3E%3C/svg%3E"); +} + +.kelvin::after { + content: "K"; +} + +.mired::after { + content: " Mired"; +} + +.percent::after { + content: "%"; +} + +.sdpi-item-value + .icon-cooler, +.sdpi-item-value + .icon-warmer { + margin-left: 0px !important; + margin-top: 15px !important; +} + +/** + CONTROL-CENTER STYLES +*/ +input[type="range"].colorbrightness::-webkit-slider-runnable-track, +input[type="range"].colortemperature::-webkit-slider-runnable-track { + height: 8px; + background: #979797; + border-radius: 4px; + background-image: linear-gradient(to right,#94d0ec, #ffb165); +} + +input[type="range"].colorbrightness::-webkit-slider-runnable-track { + background-color: #efefef; + background-image: linear-gradient(to right, black , rgba(0,0,0,0)); +} + + +input[type="range"].colorbrightness::-webkit-slider-thumb, +input[type="range"].colortemperature::-webkit-slider-thumb { + width: 16px; + height: 16px; + border-radius: 20px; + margin-top: -5px; + background-color: #86c6e8; + box-shadow: 0px 0px 1px #000000; + border: 1px solid #d8d8d8; +} +.sdpi-info-label { + display: inline-block; + user-select: none; + position: absolute; + height: 15px; + width: auto; + text-align: center; + border-radius: 4px; + min-width: 44px; + max-width: 80px; + background: white; + font-size: 11px; + color: black; + z-index: 1000; + box-shadow: 0px 0px 12px rgba(0,0,0,.8); + padding: 2px; + +} + +.sdpi-info-label.hidden { + opacity: 0; + transition: opacity 0.25s linear; +} + +.sdpi-info-label.shown { + position: absolute; + opacity: 1; + transition: opacity 0.25s ease-out; +} \ No newline at end of file diff --git a/plugin/pi/index_pi.html b/plugin/pi/index_pi.html new file mode 100644 index 0000000..66191b1 --- /dev/null +++ b/plugin/pi/index_pi.html @@ -0,0 +1,44 @@ + + + + + + + + + Property Inspector Samples PI + + + + + + +

+ +
+
IP/Host
+ +
+ +
+
Port
+ +
+ +
+
Password
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/plugin/pi/index_pi.js b/plugin/pi/index_pi.js new file mode 100644 index 0000000..e1f63bf --- /dev/null +++ b/plugin/pi/index_pi.js @@ -0,0 +1,324 @@ +// this is our global websocket, used to communicate from/to Stream Deck software +// and some info about our plugin, as sent by Stream Deck software +var websocket = null, + uuid = null, + actionInfo = {}, + settings = {}, + globalSettings = {}, + isQT = navigator.appVersion.includes('QtWebEngine'); // 'oninput'; // change this, if you want interactive elements act on any change, or while they're modified + +const websiteAction = 'tf.meow.remote.website'; + +function connectSocket ( + inPort, + inUUID, + inMessageType, + inApplicationInfo, + inActionInfo +) { + connectElgatoStreamDeckSocket( + inPort, + inUUID, + inMessageType, + inApplicationInfo, + inActionInfo + ); +} + +function connectElgatoStreamDeckSocket (inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) { + uuid = inUUID; + // please note: the incoming arguments are of type STRING, so + // in case of the inActionInfo, we must parse it into JSON first + actionInfo = JSON.parse(inActionInfo); // cache the info + inInfo = JSON.parse(inInfo); + websocket = new WebSocket('ws://localhost:' + inPort); + + /** Since the PI doesn't have access to your OS native settings + * Stream Deck sends some color settings to PI + * We use these to adjust some styles (e.g. highlight-colors for checkboxes) + */ + addDynamicStyles(inInfo.colors, 'connectElgatoStreamDeckSocket'); + + /** let's see, if we have some settings */ + settings = getPropFromString(actionInfo, 'payload.settings'); + console.log(settings, actionInfo); + initPropertyInspector(5); + + // if connection was established, the websocket sends + // an 'onopen' event, where we need to register our PI + websocket.onopen = function () { + var json = { + event: inRegisterEvent, + uuid: inUUID + }; + + websocket.send(JSON.stringify(json)); + }; + + websocket.onmessage = function (evt) { + // Received message from Stream Deck + let jsonObj = JSON.parse(evt.data); + + let event = jsonObj['event']; + + console.log('Got event', event); + + switch (event) { + case 'didReceiveGlobalSettings': + didReceiveGlobalSettings(jsonObj); + break; + } + }; +} + +function initPropertyInspector(initDelay) { + const action = actionInfo['action']; + + $('[data-action="' + action + '"]').removeClass('hidden'); + + Object.keys(settings).forEach(function (item) { + let $item = $('#' + item), + value = settings[item]; + + switch ($item.attr('type')) { + case 'checkbox': + let itemVal = $item.attr('value'); + + if (itemVal == 'false' || itemVal == 'true') { + itemVal = (/^true$/i).test(itemVal); + } + + if (itemVal === value) { + $item.prop('checked', true); + } + break; + default: + $item.val(value); + } + }); + + $('input').each(function() { + let $this = $(this), + id = $this.attr('id'); + + let $item = $this.closest('.sdpi-item'); + + $this.on('change', function() { + const type = $this.attr('type'); + + let val = $this.val(); + + switch (type) { + case 'checkbox': + // If unchecked, unset the setting + if (!this.checked) { + removeSetting(id); + return; + } + + if (val == 'false' || val == 'true') { + val = (/^true$/i).test(val); + } + break; + case 'file': + const info = $item.find('.sdpi-file-info'); + + if (info) { + const s = decodeURIComponent($this.val().replace(/^C:\\fakepath\\/, '')).split('/').pop(); + + info.text(s.length > 28 + ? s.substr(0, 10) + + '...' + + s.substr(s.length - 10, s.length) + : s); + } + break; + } + + updateSetting(id, val); + }); + }); +} + +if (!isQT) { + document.addEventListener('DOMContentLoaded', function () { + initPropertyInspector(100); + }); +} + +/** Stream Deck software passes system-highlight color information + * to Property Inspector. Here we 'inject' the CSS styles into the DOM + * when we receive this information. */ +function addDynamicStyles (clrs, fromWhere) { + const node = document.getElementById('#sdpi-dynamic-styles') || document.createElement('style'); + if (!clrs.mouseDownColor) clrs.mouseDownColor = fadeColor(clrs.highlightColor, -100); + const clr = clrs.highlightColor.slice(0, 7); + const clr1 = fadeColor(clr, 100); + const clr2 = fadeColor(clr, 60); + const metersActiveColor = fadeColor(clr, -60); + + node.setAttribute('id', 'sdpi-dynamic-styles'); + node.innerHTML = ` + + input[type="radio"]:checked + label span, + input[type="checkbox"]:checked + label span { + background-color: ${clrs.highlightColor}; + } + + input[type="radio"]:active:checked + label span, + input[type="radio"]:active + label span, + input[type="checkbox"]:active:checked + label span, + input[type="checkbox"]:active + label span { + background-color: ${clrs.mouseDownColor}; + } + + input[type="radio"]:active + label span, + input[type="checkbox"]:active + label span { + background-color: ${clrs.buttonPressedBorderColor}; + } + + td.selected, + td.selected:hover, + li.selected:hover, + li.selected { + color: white; + background-color: ${clrs.highlightColor}; + } + + .sdpi-file-label > label:active, + .sdpi-file-label.file:active, + label.sdpi-file-label:active, + label.sdpi-file-info:active, + input[type="file"]::-webkit-file-upload-button:active, + button:active { + background-color: ${clrs.buttonPressedBackgroundColor}; + color: ${clrs.buttonPressedTextColor}; + border-color: ${clrs.buttonPressedBorderColor}; + } + + ::-webkit-progress-value, + meter::-webkit-meter-optimum-value { + background: linear-gradient(${clr2}, ${clr1} 20%, ${clr} 45%, ${clr} 55%, ${clr2}) + } + + ::-webkit-progress-value:active, + meter::-webkit-meter-optimum-value:active { + background: linear-gradient(${clr}, ${clr2} 20%, ${metersActiveColor} 45%, ${metersActiveColor} 55%, ${clr}) + } + `; + document.body.appendChild(node); +}; + +/** UTILITIES */ + +/** get a JSON property from a (dot-separated) string + * Works on nested JSON, e.g.: + * jsn = { + * propA: 1, + * propB: 2, + * propC: { + * subA: 3, + * subB: { + * testA: 5, + * testB: 'Hello' + * } + * } + * } + * getPropFromString(jsn,'propC.subB.testB') will return 'Hello'; + */ +const getPropFromString = (jsn, str, sep = '.') => { + const arr = str.split(sep); + return arr.reduce((obj, key) => + (obj && obj.hasOwnProperty(key)) ? obj[key] : undefined, jsn); +}; + +/* + Quick utility to lighten or darken a color (doesn't take color-drifting, etc. into account) + Usage: + fadeColor('#061261', 100); // will lighten the color + fadeColor('#200867'), -100); // will darken the color +*/ +function fadeColor (col, amt) { + const min = Math.min, max = Math.max; + const num = parseInt(col.replace(/#/g, ''), 16); + const r = min(255, max((num >> 16) + amt, 0)); + const g = min(255, max((num & 0x0000FF) + amt, 0)); + const b = min(255, max(((num >> 8) & 0x00FF) + amt, 0)); + return '#' + (g | (b << 8) | (r << 16)).toString(16).padStart(6, 0); +} + +function updateSetting(setting, value) { + if (!settings) { + settings = {}; + } + + settings[setting] = value; + + setSettings(settings); +} + +function removeSetting(setting) { + if (!settings) { + settings = {}; + } + + delete settings[setting]; + + setSettings(settings); +} + +function setSettings(settings) { + let json = { + "event": "setSettings", + "context": uuid, + "payload": settings + }; + + if (websocket) { + websocket.send(JSON.stringify(json)); + } +} + +function updateGlobalSetting(id, val) { + globalSettings[id] = val; + + setGlobalSettings(globalSettings); +} + +function getGlobalSettings() { + let json = { + "event": "getGlobalSettings", + "context": uuid + }; + + if (websocket) { + websocket.send(JSON.stringify(json)); + } +} + +function setGlobalSettings(settings) { + let json = { + "event": "setGlobalSettings", + "context": uuid, + "payload": settings + }; + + if (websocket) { + websocket.send(JSON.stringify(json)); + } +} + +function didReceiveGlobalSettings(obj) { + globalSettings = getPropFromString(obj, 'payload.settings'); + + // Load defaults for fields not set + Object.keys(globalSettings).forEach(function(item) { + if (!(item in settings)) { + $('#' + item).val(globalSettings[item]); + } + }); +} + +function isAction(action) { + return actionInfo['action'] === action; +} \ No newline at end of file diff --git a/replay.go b/replay.go new file mode 100644 index 0000000..f00b5ef --- /dev/null +++ b/replay.go @@ -0,0 +1,256 @@ +package main + +import ( + "github.com/valyala/fastjson" + "log" + "meow.tf/streamdeck/obs-replay/obsws" + "meow.tf/streamdeck/sdk" + "strconv" + "sync" +) + +const ( + actionReplayToggle = "tf.meow.obsreplay.replay_toggle" + actionReplaySave = "tf.meow.obsreplay.replay_save" +) + +var ( + contextMutex sync.RWMutex + clientMutex sync.RWMutex + stateMutex sync.RWMutex + + clients = make(map[string]*obsws.Client) + contexts = make(map[string]string) + cachedStates = make(map[string]int) +) + +func replayToggle(action, context string, payload *fastjson.Value, deviceId string) { + c := clientForContext(context) + + if c == nil { + sdk.ShowAlert(context) + return + } + + req := obsws.NewStartStopReplayBufferRequest() + + err := req.Send(c) + + if err != nil { + sdk.ShowAlert(context) + return + } + + sdk.ShowOk(context) +} + +func replaySave(action, context string, payload *fastjson.Value, deviceId string) { + c := clientForContext(context) + + if c == nil { + sdk.ShowAlert(context) + return + } + + req := obsws.NewSaveReplayBufferRequest() + + if err := req.Send(c); err != nil { + sdk.ShowAlert(context) + return + } + + sdk.ShowOk(context) +} + +func clientForContext(context string) *obsws.Client { + contextMutex.RLock() + key, exists := contexts[context] + contextMutex.RUnlock() + + if !exists { + return nil + } + + clientMutex.RLock() + c, exists := clients[key] + clientMutex.RUnlock() + + if !exists { + return nil + } + + return c +} + +func onWillAppear(e *sdk.WillAppearEvent) { + settings := e.Payload.Get("settings") + + if settings != nil { + host := sdk.JsonStringValue(settings, "host") + port := settings.GetInt("port") + password := sdk.JsonStringValue(settings, "password") + + key := checkClient(host, port, password) + + if key == "" { + return + } + + contextMutex.Lock() + contexts[e.Context] = key + contextMutex.Unlock() + + stateMutex.RLock() + if state, ok := cachedStates[key]; ok { + sdk.SetState(e.Context, state) + } + stateMutex.RUnlock() + } +} + +func checkClient(host string, port int, password string) string { + if port == 0 { + port = 4444 + } + + key := host + ":" + strconv.Itoa(port) + + clientMutex.RLock() + client, ok := clients[key] + clientMutex.RUnlock() + + if !ok { + client = &obsws.Client{Host: host, Port: port, Password: password} + + err := client.Connect() + + if err != nil { + return "" + } + + client.AddEventHandler("StreamStatus", streamStatusUpdate(key)) + client.AddEventHandler("ReplayStarted", loopContextState(key, 1)) + client.AddEventHandler("ReplayStopped", loopContextState(key, 0)) + + clientMutex.Lock() + clients[key] = client + clientMutex.Unlock() + } + + return key +} + +func streamStatusUpdate(key string) func(obsws.Event) { + return func(e obsws.Event) { + evt := e.(obsws.StreamStatusEvent) + + state := 0 + + if evt.Replay { + state = 1 + } + + loopContextState(key, state) + } +} + +func loopContextState(key string, state int) func(obsws.Event) { + stateMutex.Lock() + cachedStates[key] = state + stateMutex.Unlock() + + return func(event obsws.Event) { + contextMutex.RLock() + defer contextMutex.RUnlock() + + for ctx, ctxKey := range contexts { + if ctxKey == key { + sdk.SetState(ctx, state) + } + } + } +} + +func onWillDisappear(e *sdk.WillDisappearEvent) { + contextMutex.Lock() + defer contextMutex.Unlock() + + // replayToggleContexts + key, ok := contexts[e.Context] + + delete(contexts, e.Context) + + if !ok { + return + } + + for _, k := range contexts { + if k == key { + return + } + } + + clientMutex.Lock() + clients[key].Disconnect() + + delete(clients, key) + clientMutex.Unlock() +} + +func onSettingsReceived(e *sdk.ReceiveSettingsEvent) { + var host, password string + + host = sdk.JsonStringValue(e.Settings, "host") + port := e.Settings.GetInt("port") + password = sdk.JsonStringValue(e.Settings, "password") + + if port == 0 { + port = 4444 + } + + key := checkClient(host, port, password) + + if key == "" { + return + } + + contextMutex.Lock() + contexts[e.Context] = key + contextMutex.Unlock() + + stateMutex.RLock() + if state, ok := cachedStates[key]; ok { + sdk.SetState(e.Context, state) + } + stateMutex.RUnlock() +} + +func main() { + sdk.RegisterAction(actionReplayToggle, replayToggle) + sdk.RegisterAction(actionReplaySave, replaySave) + + sdk.AddHandler(onWillAppear) + sdk.AddHandler(onWillDisappear) + sdk.AddHandler(onSettingsReceived) + + // Open and connect the SDK + err := sdk.Open() + + if err != nil { + log.Fatalln(err) + } + + defer cleanupSockets() + + // Wait until the socket is closed, or SIGTERM/SIGINT is received + sdk.Wait() +} + +func cleanupSockets() { + clientMutex.RLock() + defer clientMutex.RUnlock() + + for _, client := range clients { + client.Disconnect() + } +} \ No newline at end of file diff --git a/replay_test.go b/replay_test.go new file mode 100644 index 0000000..92654c6 --- /dev/null +++ b/replay_test.go @@ -0,0 +1,21 @@ +package main + +import ( + "meow.tf/streamdeck/obs-replay/obsws" + "testing" +) + +func Test_ReplayStatus(t *testing.T) { + c := &obsws.Client{Host: "localhost", Port: 4444} + + if err := c.Connect(); err != nil { + t.Fatal(err) + } + + c.AddEventHandler("StreamStatus", func(e obsws.Event) { + // Make sure to assert the actual event type. + t.Log("status:", e.(obsws.StreamStatusEvent).Replay) + }) + + <- make(chan struct{}) +}