Add a display type for showing a Twitch stream.

This commit is contained in:
Patrick Fairbank
2018-09-17 19:26:27 -07:00
parent 6540d79038
commit 68d358c1ee
7 changed files with 157 additions and 0 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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", {
});
});

View File

@@ -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.
*/}}
<!DOCTYPE html>
<html>
<head>
<title>Twitch Stream Display - {{.EventSettings.Name}} - Cheesy Arena </title>
<link rel="shortcut icon" href="/static/img/favicon.ico">
<link rel="stylesheet" href="/static/css/twitch_display.css" />
</head>
<body>
<div id="twitchEmbed"></div>
<script src="https://embed.twitch.tv/embed/v1.js"></script>
<script src="/static/js/lib/jquery.min.js"></script>
<script src="/static/js/lib/jquery.json-2.4.min.js"></script>
<script src="/static/js/lib/jquery.websocket-0.0.1.js"></script>
<script src="/static/js/cheesy-websocket.js"></script>
<script src="/static/js/twitch_display.js"></script>
</body>
</html>

61
web/twitch_display.go Normal file
View File

@@ -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)
}

View File

@@ -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")
}

View File

@@ -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")