diff --git a/field/display.go b/field/display.go
index cb5f60b..68e244a 100644
--- a/field/display.go
+++ b/field/display.go
@@ -31,6 +31,7 @@ const (
AudienceDisplay
FieldMonitorDisplay
PitDisplay
+ TwitchStreamDisplay
)
var DisplayTypeNames = map[DisplayType]string{
@@ -40,6 +41,7 @@ var DisplayTypeNames = map[DisplayType]string{
AudienceDisplay: "Audience",
FieldMonitorDisplay: "Field Monitor",
PitDisplay: "Pit",
+ TwitchStreamDisplay: "Twitch Stream",
}
var displayTypePaths = map[DisplayType]string{
@@ -49,6 +51,7 @@ var displayTypePaths = map[DisplayType]string{
AudienceDisplay: "/displays/audience",
FieldMonitorDisplay: "/displays/fta",
PitDisplay: "/displays/pit",
+ TwitchStreamDisplay: "/displays/twitch",
}
var displayRegistryMutex sync.Mutex
diff --git a/static/css/twitch_display.css b/static/css/twitch_display.css
new file mode 100644
index 0000000..95840e3
--- /dev/null
+++ b/static/css/twitch_display.css
@@ -0,0 +1,12 @@
+/*
+ Copyright 2018 Team 254. All Rights Reserved.
+ Author: pat@patfairbank.com (Patrick Fairbank)
+*/
+
+html {
+ overflow: hidden;
+}
+body {
+ margin: 0;
+ background-color: #000;
+}
diff --git a/static/js/twitch_display.js b/static/js/twitch_display.js
new file mode 100644
index 0000000..bdee1f0
--- /dev/null
+++ b/static/js/twitch_display.js
@@ -0,0 +1,23 @@
+// Copyright 2018 Team 254. All Rights Reserved.
+// Author: pat@patfairbank.com (Patrick Fairbank)
+//
+// Client-side logic for the Twitch stream display.
+
+var websocket;
+
+$(function() {
+ // Read the configuration for this display from the URL query string.
+ var urlParams = new URLSearchParams(window.location.search);
+
+ // Embed the video stream.
+ new Twitch.Embed("twitchEmbed", {
+ channel: urlParams.get("channel"),
+ width: window.innerWidth,
+ height: window.innerHeight,
+ layout: "video"
+ });
+
+ // Set up the websocket back to the server.
+ websocket = new CheesyWebsocket("/displays/twitch/websocket", {
+ });
+});
diff --git a/templates/twitch_display.html b/templates/twitch_display.html
new file mode 100644
index 0000000..2e6d464
--- /dev/null
+++ b/templates/twitch_display.html
@@ -0,0 +1,23 @@
+{{/*
+ Copyright 2018 Team 254. All Rights Reserved.
+ Author: pat@patfairbank.com (Patrick Fairbank)
+
+ Display to show a configurable Twitch live video stream.
+*/}}
+
+
+
+ Twitch Stream Display - {{.EventSettings.Name}} - Cheesy Arena
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/twitch_display.go b/web/twitch_display.go
new file mode 100644
index 0000000..0767659
--- /dev/null
+++ b/web/twitch_display.go
@@ -0,0 +1,61 @@
+// Copyright 2018 Team 254. All Rights Reserved.
+// Author: pat@patfairbank.com (Patrick Fairbank)
+//
+// Web routes for a display to show a configurable Twitch live video stream.
+
+package web
+
+import (
+ "github.com/Team254/cheesy-arena/model"
+ "github.com/Team254/cheesy-arena/websocket"
+ "net/http"
+)
+
+// Renders the Twitch stream view.
+func (web *Web) twitchDisplayHandler(w http.ResponseWriter, r *http.Request) {
+ if !web.userIsReader(w, r) {
+ return
+ }
+
+ if !web.enforceDisplayConfiguration(w, r, map[string]string{"channel": "team254"}) {
+ return
+ }
+
+ template, err := web.parseFiles("templates/twitch_display.html")
+ if err != nil {
+ handleWebErr(w, err)
+ return
+ }
+ data := struct {
+ *model.EventSettings
+ }{web.arena.EventSettings}
+ err = template.ExecuteTemplate(w, "twitch_display.html", data)
+ if err != nil {
+ handleWebErr(w, err)
+ return
+ }
+}
+
+// The websocket endpoint for sending configuration commands to the display.
+func (web *Web) twitchDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
+ if !web.userIsReader(w, r) {
+ return
+ }
+
+ display, err := web.registerDisplay(r)
+ if err != nil {
+ handleWebErr(w, err)
+ return
+ }
+ defer web.arena.MarkDisplayDisconnected(display)
+
+ ws, err := websocket.NewWebsocket(w, r)
+ if err != nil {
+ handleWebErr(w, err)
+ return
+ }
+ defer ws.Close()
+
+ // Subscribe the websocket to the notifiers whose messages will be passed on to the client.
+ ws.HandleNotifiers(web.arena.DisplayConfigurationNotifier, web.arena.ReloadDisplaysNotifier)
+}
diff --git a/web/twitch_display_test.go b/web/twitch_display_test.go
new file mode 100644
index 0000000..4f340b1
--- /dev/null
+++ b/web/twitch_display_test.go
@@ -0,0 +1,33 @@
+// Copyright 2018 Team 254. All Rights Reserved.
+// Author: pat@patfairbank.com (Patrick Fairbank)
+
+package web
+
+import (
+ "github.com/Team254/cheesy-arena/websocket"
+ gorillawebsocket "github.com/gorilla/websocket"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestTwitchDisplay(t *testing.T) {
+ web := setupTestWeb(t)
+
+ recorder := web.getHttpResponse("/displays/twitch?displayId=1&channel=team254")
+ assert.Equal(t, 200, recorder.Code)
+ assert.Contains(t, recorder.Body.String(), "Twitch Stream Display - Untitled Event - Cheesy Arena")
+}
+
+func TestTwitchDisplayWebsocket(t *testing.T) {
+ web := setupTestWeb(t)
+
+ server, wsUrl := web.startTestServer()
+ defer server.Close()
+ conn, _, err := gorillawebsocket.DefaultDialer.Dial(wsUrl+"/displays/twitch/websocket?displayId=123", nil)
+ assert.Nil(t, err)
+ defer conn.Close()
+ ws := websocket.NewTestWebsocket(conn)
+
+ // Should get a few status updates right after connection.
+ readWebsocketType(t, ws, "displayConfiguration")
+}
diff --git a/web/web.go b/web/web.go
index ba8c729..1204037 100644
--- a/web/web.go
+++ b/web/web.go
@@ -148,6 +148,8 @@ func (web *Web) newHandler() http.Handler {
router.HandleFunc("/displays/fta/websocket", web.ftaDisplayWebsocketHandler).Methods("GET")
router.HandleFunc("/displays/pit", web.pitDisplayHandler).Methods("GET")
router.HandleFunc("/displays/pit/websocket", web.pitDisplayWebsocketHandler).Methods("GET")
+ router.HandleFunc("/displays/twitch", web.twitchDisplayHandler).Methods("GET")
+ router.HandleFunc("/displays/twitch/websocket", web.twitchDisplayWebsocketHandler).Methods("GET")
router.HandleFunc("/match_play", web.matchPlayHandler).Methods("GET")
router.HandleFunc("/match_play/{matchId}/load", web.matchPlayLoadHandler).Methods("GET")
router.HandleFunc("/match_play/{matchId}/show_result", web.matchPlayShowResultHandler).Methods("GET")