From def8c0e8afca423202bd6c894a08bc93d40ba530 Mon Sep 17 00:00:00 2001 From: Patrick Fairbank Date: Wed, 22 May 2024 20:24:27 -0700 Subject: [PATCH] Add a display which shows an arbitrary web page (closes #140). --- field/display.go | 3 ++ static/css/webpage_display.css | 20 +++++++++++++ static/js/webpage_display.js | 17 +++++++++++ templates/base.html | 1 + templates/webpage_display.html | 22 ++++++++++++++ web/web.go | 2 ++ web/webpage_display.go | 53 ++++++++++++++++++++++++++++++++++ web/webpage_display_test.go | 33 +++++++++++++++++++++ 8 files changed, 151 insertions(+) create mode 100644 static/css/webpage_display.css create mode 100644 static/js/webpage_display.js create mode 100644 templates/webpage_display.html create mode 100644 web/webpage_display.go create mode 100644 web/webpage_display_test.go diff --git a/field/display.go b/field/display.go index 7b08a549..046131e3 100644 --- a/field/display.go +++ b/field/display.go @@ -36,6 +36,7 @@ const ( RankingsDisplay TwitchStreamDisplay WallDisplay + WebpageDisplay ) var DisplayTypeNames = map[DisplayType]string{ @@ -49,6 +50,7 @@ var DisplayTypeNames = map[DisplayType]string{ RankingsDisplay: "Rankings", TwitchStreamDisplay: "Twitch Stream", WallDisplay: "Wall", + WebpageDisplay: "Web Page", } var displayTypePaths = map[DisplayType]string{ @@ -62,6 +64,7 @@ var displayTypePaths = map[DisplayType]string{ RankingsDisplay: "/displays/rankings", TwitchStreamDisplay: "/displays/twitch", WallDisplay: "/displays/wall", + WebpageDisplay: "/displays/webpage", } var displayRegistryMutex sync.Mutex diff --git a/static/css/webpage_display.css b/static/css/webpage_display.css new file mode 100644 index 00000000..a06effdf --- /dev/null +++ b/static/css/webpage_display.css @@ -0,0 +1,20 @@ +/* + Copyright 2024 Team 254. All Rights Reserved. + Author: pat@patfairbank.com (Patrick Fairbank) +*/ + +html { + overflow: hidden; +} +body { + margin: 0; + background-color: #000; +} +#webpageFrame { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: none; +} diff --git a/static/js/webpage_display.js b/static/js/webpage_display.js new file mode 100644 index 00000000..327c4b17 --- /dev/null +++ b/static/js/webpage_display.js @@ -0,0 +1,17 @@ +// Copyright 2024 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Client-side logic for the web page display. + +var websocket; + +$(function() { + // Read the configuration for this display from the URL query string. + var urlParams = new URLSearchParams(window.location.search); + console.log(urlParams.get("url")); + $("#webpageFrame").attr("src", urlParams.get("url")); + + // Set up the websocket back to the server. + websocket = new CheesyWebsocket("/displays/webpage/websocket", { + }); +}); diff --git a/templates/base.html b/templates/base.html index 22c3ea08..5a033468 100644 --- a/templates/base.html +++ b/templates/base.html @@ -92,6 +92,7 @@ Queueing Standings Wall + Web Page Red 1 diff --git a/templates/webpage_display.html b/templates/webpage_display.html new file mode 100644 index 00000000..b13ac492 --- /dev/null +++ b/templates/webpage_display.html @@ -0,0 +1,22 @@ +{{/* + Copyright 2024 Team 254. All Rights Reserved. + Author: pat@patfairbank.com (Patrick Fairbank) + + Display to show an arbitrary web page. +*/}} + + + + Web Page Display - {{.EventSettings.Name}} - Cheesy Arena + + + + + + + + + + + + diff --git a/web/web.go b/web/web.go index cc72b01f..55a9a4fe 100644 --- a/web/web.go +++ b/web/web.go @@ -157,6 +157,8 @@ func (web *Web) newHandler() http.Handler { mux.HandleFunc("GET /displays/twitch/websocket", web.twitchDisplayWebsocketHandler) mux.HandleFunc("GET /displays/wall", web.wallDisplayHandler) mux.HandleFunc("GET /displays/wall/websocket", web.wallDisplayWebsocketHandler) + mux.HandleFunc("GET /displays/webpage", web.webpageDisplayHandler) + mux.HandleFunc("GET /displays/webpage/websocket", web.webpageDisplayWebsocketHandler) mux.HandleFunc("GET /login", web.loginHandler) mux.HandleFunc("POST /login", web.loginPostHandler) mux.HandleFunc("GET /match_play", web.matchPlayHandler) diff --git a/web/webpage_display.go b/web/webpage_display.go new file mode 100644 index 00000000..8bc31aad --- /dev/null +++ b/web/webpage_display.go @@ -0,0 +1,53 @@ +// Copyright 2024 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Web routes for a display to show an arbitrary web page. + +package web + +import ( + "github.com/Team254/cheesy-arena/model" + "github.com/Team254/cheesy-arena/websocket" + "net/http" +) + +// Renders the web page view. +func (web *Web) webpageDisplayHandler(w http.ResponseWriter, r *http.Request) { + if !web.enforceDisplayConfiguration(w, r, map[string]string{"url": "https://www.team254.com"}) { + return + } + + template, err := web.parseFiles("templates/webpage_display.html") + if err != nil { + handleWebErr(w, err) + return + } + data := struct { + *model.EventSettings + }{web.arena.EventSettings} + err = template.ExecuteTemplate(w, "webpage_display.html", data) + if err != nil { + handleWebErr(w, err) + return + } +} + +// The websocket endpoint for sending configuration commands to the display. +func (web *Web) webpageDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { + display, err := web.registerDisplay(r) + if err != nil { + handleWebErr(w, err) + return + } + defer web.arena.MarkDisplayDisconnected(display.DisplayConfiguration.Id) + + 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(display.Notifier, web.arena.ReloadDisplaysNotifier) +} diff --git a/web/webpage_display_test.go b/web/webpage_display_test.go new file mode 100644 index 00000000..07675671 --- /dev/null +++ b/web/webpage_display_test.go @@ -0,0 +1,33 @@ +// Copyright 2024 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 TestWebpageDisplay(t *testing.T) { + web := setupTestWeb(t) + + recorder := web.getHttpResponse("/displays/webpage?displayId=1&url=https://www.team254.com") + assert.Equal(t, 200, recorder.Code) + assert.Contains(t, recorder.Body.String(), "Web Page Display - Untitled Event - Cheesy Arena") +} + +func TestWebpageDisplayWebsocket(t *testing.T) { + web := setupTestWeb(t) + + server, wsUrl := web.startTestServer() + defer server.Close() + conn, _, err := gorillawebsocket.DefaultDialer.Dial(wsUrl+"/displays/webpage/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") +}