From f187ffbd89117dfffea43f88f79eb874c5b64153 Mon Sep 17 00:00:00 2001 From: Jack Doherty Date: Mon, 3 Jun 2024 13:46:49 -0700 Subject: [PATCH 1/5] added fields to settings for blackmagic recorder ip/port --- field/arena.go | 5 +++++ model/event_settings.go | 2 ++ templates/setup_settings.html | 18 ++++++++++++++++++ web/setup_settings.go | 2 ++ 4 files changed, 27 insertions(+) diff --git a/field/arena.go b/field/arena.go index b9a1b150..e3431fd9 100644 --- a/field/arena.go +++ b/field/arena.go @@ -1077,3 +1077,8 @@ func (arena *Arena) runPeriodicTasks() { arena.updateEarlyLateMessage() arena.purgeDisconnectedDisplays() } + +// Starts recording using configured, on-network Blackmagic Device +func (arena *Arena) startRecording() { + //code +} diff --git a/model/event_settings.go b/model/event_settings.go index a34aa042..23aa3b6f 100644 --- a/model/event_settings.go +++ b/model/event_settings.go @@ -43,6 +43,8 @@ type EventSettings struct { TeamSignBlue2Address string TeamSignBlue3Address string TeamSignBlueTimerAddress string + RecorderAddress string + RecorderPort int WarmupDurationSec int AutoDurationSec int PauseDurationSec int diff --git a/templates/setup_settings.html b/templates/setup_settings.html index 998c9c41..d186dd99 100644 --- a/templates/setup_settings.html +++ b/templates/setup_settings.html @@ -282,6 +282,24 @@ +
+ Instant Replay Match Recording +

+ If you are using a Blackmagic HyperDeck device to record matches, enter device information here. +

+
+ +
+ +
+
+
+ +
+ +
+
+
Game-Specific
diff --git a/web/setup_settings.go b/web/setup_settings.go index 07fc3487..da069884 100644 --- a/web/setup_settings.go +++ b/web/setup_settings.go @@ -93,6 +93,8 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) { eventSettings.TeamSignBlue2Address = r.PostFormValue("teamSignBlue2Address") eventSettings.TeamSignBlue3Address = r.PostFormValue("teamSignBlue3Address") eventSettings.TeamSignBlueTimerAddress = r.PostFormValue("teamSignBlueTimerAddress") + eventSettings.RecorderAddress = r.PostFormValue("recorderAddress") + eventSettings.RecorderPort, _ = strconv.Atoi(r.PostFormValue("recorderPort")) eventSettings.WarmupDurationSec, _ = strconv.Atoi(r.PostFormValue("warmupDurationSec")) eventSettings.AutoDurationSec, _ = strconv.Atoi(r.PostFormValue("autoDurationSec")) eventSettings.PauseDurationSec, _ = strconv.Atoi(r.PostFormValue("pauseDurationSec")) From 8d8162151e9e9876b0a282813aaf14cfae267113 Mon Sep 17 00:00:00 2001 From: Jack Doherty Date: Tue, 25 Jun 2024 18:43:55 -0700 Subject: [PATCH 2/5] added recording --- field/arena.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/field/arena.go b/field/arena.go index 292ba770..cb15a91a 100644 --- a/field/arena.go +++ b/field/arena.go @@ -9,6 +9,7 @@ import ( "fmt" "log" "math" + "net" "reflect" "time" @@ -459,6 +460,7 @@ func (arena *Arena) AbortMatch() error { arena.AudienceDisplayModeNotifier.Notify() arena.AllianceStationDisplayMode = "logo" arena.AllianceStationDisplayModeNotifier.Notify() + arena.stopRecording() return nil } @@ -548,6 +550,7 @@ func (arena *Arena) Update() { arena.AudienceDisplayModeNotifier.Notify() arena.AllianceStationDisplayMode = "match" arena.AllianceStationDisplayModeNotifier.Notify() + arena.startRecording() if game.MatchTiming.WarmupDurationSec > 0 { arena.MatchState = WarmupPeriod enabled = false @@ -599,6 +602,7 @@ func (arena *Arena) Update() { auto = false enabled = false sendDsPacket = true + arena.stopRecording() go func() { // Leave the scores on the screen briefly at the end of the match. time.Sleep(time.Second * matchEndScoreDwellSec) @@ -1090,5 +1094,22 @@ func (arena *Arena) runPeriodicTasks() { // Starts recording using configured, on-network Blackmagic Device func (arena *Arena) startRecording() { - //code + conn, err := net.Dial("tcp", fmt.Sprintln("%s:%d", arena.EventSettings.RecorderAddress, arena.EventSettings.RecorderPort)) + if err != nil { + //todo: actually error on screen in some way? Ask pat for advice here. + fmt.Println("error: ", err) + } + + fmt.Fprint(conn, "record\n") } + +// Stops recording using configured, on-network Blackmagic Device +func (arena *Arena) stopRecording() { + conn, err := net.Dial("tcp", fmt.Sprintln("%s:%d", arena.EventSettings.RecorderAddress, arena.EventSettings.RecorderPort)) + if err != nil { + //todo: actually error on screen in some way? Ask pat for advice here. + fmt.Println("error: ", err) + } + + fmt.Fprint(conn, "stop\n") +} \ No newline at end of file From ddeb33307ecef36e5c4a72ed77d3c54b8a64460d Mon Sep 17 00:00:00 2001 From: Jack Doherty Date: Tue, 25 Jun 2024 19:00:47 -0700 Subject: [PATCH 3/5] fixed typo --- field/arena.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/field/arena.go b/field/arena.go index cb15a91a..c586c380 100644 --- a/field/arena.go +++ b/field/arena.go @@ -1094,7 +1094,7 @@ func (arena *Arena) runPeriodicTasks() { // Starts recording using configured, on-network Blackmagic Device func (arena *Arena) startRecording() { - conn, err := net.Dial("tcp", fmt.Sprintln("%s:%d", arena.EventSettings.RecorderAddress, arena.EventSettings.RecorderPort)) + conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", arena.EventSettings.RecorderAddress, arena.EventSettings.RecorderPort)) if err != nil { //todo: actually error on screen in some way? Ask pat for advice here. fmt.Println("error: ", err) @@ -1105,11 +1105,11 @@ func (arena *Arena) startRecording() { // Stops recording using configured, on-network Blackmagic Device func (arena *Arena) stopRecording() { - conn, err := net.Dial("tcp", fmt.Sprintln("%s:%d", arena.EventSettings.RecorderAddress, arena.EventSettings.RecorderPort)) + conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", arena.EventSettings.RecorderAddress, arena.EventSettings.RecorderPort)) if err != nil { //todo: actually error on screen in some way? Ask pat for advice here. fmt.Println("error: ", err) } fmt.Fprint(conn, "stop\n") -} \ No newline at end of file +} From 3ce93a585967b9ca263dde92ac7a664e9836ccf7 Mon Sep 17 00:00:00 2001 From: Jack Doherty Date: Sun, 30 Jun 2024 21:58:09 -0700 Subject: [PATCH 4/5] added support for multiple devices. records and stops in sync with match. --- field/arena.go | 57 +++++++++++++++++++++++++++++------ model/event_settings.go | 4 +-- templates/setup_settings.html | 9 ++---- web/setup_settings.go | 14 +++++++-- 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/field/arena.go b/field/arena.go index c586c380..8c8c4bc9 100644 --- a/field/arena.go +++ b/field/arena.go @@ -1092,24 +1092,61 @@ func (arena *Arena) runPeriodicTasks() { arena.purgeDisconnectedDisplays() } +// Connect to Blackmagic device. Returns connection object. +func connectToRecorder(connString string) (net.Conn, error) { + const MAX_CONN_ATTEMPTS = 3 + const CONN_TIMEOUT = time.Millisecond * 100 + + var err error + var conn net.Conn + + for i := 0; i < MAX_CONN_ATTEMPTS; i++ { + conn, err = net.DialTimeout("tcp", connString, CONN_TIMEOUT) + if err != nil { + log.Println("CONNECTION ERROR*******", "connString:***", connString, "***error: ", err) + } else { + return conn, nil + } + } + + return nil, err +} + // Starts recording using configured, on-network Blackmagic Device func (arena *Arena) startRecording() { - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", arena.EventSettings.RecorderAddress, arena.EventSettings.RecorderPort)) - if err != nil { - //todo: actually error on screen in some way? Ask pat for advice here. - fmt.Println("error: ", err) + var connections []net.Conn + + println("heyoooo") + fmt.Printf("%q\n", arena.EventSettings.RecorderAddresses) + + for i := 0; i < len(arena.EventSettings.RecorderAddresses); i++ { + recCon, err := connectToRecorder(arena.EventSettings.RecorderAddresses[i]) + if recCon == nil { + log.Println("recorder connection error: ", err) + } else { + connections = append(connections, recCon) + } } - fmt.Fprint(conn, "record\n") + for i := 0; i < len(connections); i++ { + fmt.Fprint(connections[i], "record\n") + } } // Stops recording using configured, on-network Blackmagic Device func (arena *Arena) stopRecording() { - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", arena.EventSettings.RecorderAddress, arena.EventSettings.RecorderPort)) - if err != nil { - //todo: actually error on screen in some way? Ask pat for advice here. - fmt.Println("error: ", err) + var connections []net.Conn + + for i := 0; i < len(arena.EventSettings.RecorderAddresses); i++ { + recCon, err := connectToRecorder(arena.EventSettings.RecorderAddresses[i]) + if recCon == nil { + log.Println("recorder connection error: ", err) + } else { + connections = append(connections, recCon) + } } - fmt.Fprint(conn, "stop\n") + for i := 0; i < len(connections); i++ { + fmt.Fprint(connections[i], "stop\n") + } } diff --git a/model/event_settings.go b/model/event_settings.go index 34a7f9c5..b0c9a7cd 100644 --- a/model/event_settings.go +++ b/model/event_settings.go @@ -44,8 +44,8 @@ type EventSettings struct { TeamSignBlue2Address string TeamSignBlue3Address string TeamSignBlueTimerAddress string - RecorderAddress string - RecorderPort int + RecorderAddressesRaw string + RecorderAddresses []string WarmupDurationSec int AutoDurationSec int PauseDurationSec int diff --git a/templates/setup_settings.html b/templates/setup_settings.html index 4d65db58..b5e51d79 100644 --- a/templates/setup_settings.html +++ b/templates/setup_settings.html @@ -295,17 +295,12 @@ Instant Replay Match Recording

If you are using a Blackmagic HyperDeck device to record matches, enter device information here. + Format entries as ip_address:port (e.g. 192.168.0.123:9993). One entry per line.

- -
-
-
- -
- +
diff --git a/web/setup_settings.go b/web/setup_settings.go index a6978e5d..d003fbad 100644 --- a/web/setup_settings.go +++ b/web/setup_settings.go @@ -11,6 +11,7 @@ import ( "io/ioutil" "net/http" "os" + "regexp" "strconv" "strings" "time" @@ -94,8 +95,17 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) { eventSettings.TeamSignBlue2Address = r.PostFormValue("teamSignBlue2Address") eventSettings.TeamSignBlue3Address = r.PostFormValue("teamSignBlue3Address") eventSettings.TeamSignBlueTimerAddress = r.PostFormValue("teamSignBlueTimerAddress") - eventSettings.RecorderAddress = r.PostFormValue("recorderAddress") - eventSettings.RecorderPort, _ = strconv.Atoi(r.PostFormValue("recorderPort")) + + eventSettings.RecorderAddressesRaw = r.PostFormValue("recorderAddresses") + const recAddrEntryPattern = "^(?:\\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):(?:6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|[0-9])\\b(?:\r?\n|$))+$" + match, _ := regexp.MatchString(recAddrEntryPattern, eventSettings.RecorderAddressesRaw) + if match { + eventSettings.RecorderAddresses = strings.Split(strings.ReplaceAll(eventSettings.RecorderAddressesRaw, "\r", ""), "\n") + } else { + web.renderSettings(w, r, "Recorder address entry error. Please ensure all addresses entered correctly.") + return + } + eventSettings.WarmupDurationSec, _ = strconv.Atoi(r.PostFormValue("warmupDurationSec")) eventSettings.AutoDurationSec, _ = strconv.Atoi(r.PostFormValue("autoDurationSec")) eventSettings.PauseDurationSec, _ = strconv.Atoi(r.PostFormValue("pauseDurationSec")) From 8b13f8d41f8cdbacd51af48cd68e494315d81555 Mon Sep 17 00:00:00 2001 From: Jack Doherty Date: Sun, 30 Jun 2024 22:17:18 -0700 Subject: [PATCH 5/5] removed debug log statements --- field/arena.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/field/arena.go b/field/arena.go index 8c8c4bc9..32dd0149 100644 --- a/field/arena.go +++ b/field/arena.go @@ -1116,9 +1116,6 @@ func connectToRecorder(connString string) (net.Conn, error) { func (arena *Arena) startRecording() { var connections []net.Conn - println("heyoooo") - fmt.Printf("%q\n", arena.EventSettings.RecorderAddresses) - for i := 0; i < len(arena.EventSettings.RecorderAddresses); i++ { recCon, err := connectToRecorder(arena.EventSettings.RecorderAddresses[i]) if recCon == nil {