streamdeck-obs-replay/replay.go

304 lines
5.1 KiB
Go

package main
import (
"github.com/valyala/fastjson"
"log"
"meow.tf/streamdeck/sdk"
"strconv"
"sync"
"time"
)
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]Client)
contexts = make(map[string]string)
contextActions = 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
}
if err := c.ToggleReplay(); err != nil {
sdk.ShowAlert(context)
return
}
time.AfterFunc(1*time.Second, func() {
contextMutex.RLock()
key, exists := contexts[context]
contextMutex.RUnlock()
if !exists {
return
}
stateMutex.RLock()
state, exists := cachedStates[key]
stateMutex.RUnlock()
if !exists {
return
}
sdk.SetState(context, state)
})
}
func replaySave(action, context string, payload *fastjson.Value, deviceId string) {
c := clientForContext(context)
if c == nil {
sdk.ShowAlert(context)
return
}
contextMutex.RLock()
key, exists := contexts[context]
contextMutex.RUnlock()
if exists {
stateMutex.RLock()
state, exists := cachedStates[key]
stateMutex.RUnlock()
if exists && state == 0 {
sdk.ShowAlert(context)
return
}
}
if err := c.SaveReplay(); err != nil {
sdk.ShowAlert(context)
return
}
sdk.ShowOk(context)
}
func clientForContext(context string) 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
}
if !c.Connected() {
err := c.Connect()
if err != nil {
return nil
}
}
return c
}
func onWillAppear(e *sdk.WillAppearEvent) {
settings := e.Payload.Get("settings")
if settings != nil {
host := sdk.JsonStringValue(settings, "host")
portStr := sdk.JsonStringValue(settings, "port")
password := sdk.JsonStringValue(settings, "password")
port, err := strconv.Atoi(portStr)
if err != nil {
port = 4444
}
key := checkClient(host, port, password)
if key == "" {
return
}
contextMutex.Lock()
contexts[e.Context] = key
contextActions[e.Context] = e.Action
contextMutex.Unlock()
if e.Action == actionReplayToggle {
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 host == "" {
host = "127.0.0.1"
}
if port == 0 {
port = 4444
}
key := host + ":" + strconv.Itoa(port)
clientMutex.RLock()
client, ok := clients[key]
clientMutex.RUnlock()
if !ok {
client = NewClient(key, host, port, password)
defer client.Connect()
clientMutex.Lock()
clients[key] = client
clientMutex.Unlock()
}
return key
}
func onWillDisappear(e *sdk.WillDisappearEvent) {
contextDiscounnected(e.Context)
}
func contextDiscounnected(context string) {
contextMutex.Lock()
defer contextMutex.Unlock()
// replayToggleContexts
key, ok := contexts[context]
delete(contexts, context)
delete(contextActions, 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) {
host := sdk.JsonStringValue(e.Settings, "host")
portStr := sdk.JsonStringValue(e.Settings, "port")
password := sdk.JsonStringValue(e.Settings, "password")
if host == "" {
host = "127.0.0.1"
}
port, err := strconv.Atoi(portStr)
if err != nil {
port = 4444
}
key := checkClient(host, port, password)
contextMutex.RLock()
previousKey, existing := contexts[e.Context]
contextMutex.RUnlock()
if existing && previousKey != key {
contextDiscounnected(e.Context)
}
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()
}
}
func loopContextState(key string, state int) {
stateMutex.Lock()
cachedStates[key] = state
stateMutex.Unlock()
contextMutex.RLock()
defer contextMutex.RUnlock()
for ctx, ctxKey := range contexts {
if ctxKey == key {
action := contextActions[ctx]
if action == actionReplayToggle {
sdk.SetState(ctx, state)
}
}
}
}