304 lines
5.1 KiB
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)
|
|
}
|
|
}
|
|
}
|
|
}
|