256 lines
4.4 KiB
Go
256 lines
4.4 KiB
Go
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()
|
|
}
|
|
} |