Added lower thirds.

This commit is contained in:
Patrick Fairbank
2014-08-15 20:13:02 -07:00
parent 63cc811830
commit 9e394b5ed0
14 changed files with 272 additions and 4 deletions

View File

@@ -77,6 +77,7 @@ type Arena struct {
playSoundNotifier *Notifier
allianceStationDisplayNotifier *Notifier
allianceSelectionNotifier *Notifier
lowerThirdNotifier *Notifier
audienceDisplayScreen string
allianceStationDisplays map[string]string
allianceStationDisplayScreen string
@@ -114,6 +115,7 @@ func (arena *Arena) Setup() {
arena.playSoundNotifier = NewNotifier()
arena.allianceStationDisplayNotifier = NewNotifier()
arena.allianceSelectionNotifier = NewNotifier()
arena.lowerThirdNotifier = NewNotifier()
// Load empty match as current.
arena.MatchState = PRE_MATCH

View File

@@ -23,6 +23,7 @@ type Database struct {
rankingMap *modl.DbMap
teamMap *modl.DbMap
allianceTeamMap *modl.DbMap
lowerThirdMap *modl.DbMap
}
// Opens the SQLite database at the given path, creating it if it doesn't exist, and runs any pending
@@ -75,4 +76,7 @@ func (database *Database) mapTables() {
database.allianceTeamMap = modl.NewDbMap(database.db, dialect)
database.allianceTeamMap.AddTableWithName(AllianceTeam{}, "alliance_teams").SetKeys(true, "Id")
database.lowerThirdMap = modl.NewDbMap(database.db, dialect)
database.lowerThirdMap.AddTableWithName(LowerThird{}, "lower_thirds").SetKeys(true, "Id")
}

View File

@@ -0,0 +1,9 @@
-- +goose Up
CREATE TABLE lower_thirds (
id INTEGER PRIMARY KEY,
toptext VARCHAR(255),
bottomtext VARCHAR(255)
);
-- +goose Down
DROP TABLE lower_thirds;

View File

@@ -62,6 +62,8 @@ func AudienceDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
defer close(playSoundListener)
allianceSelectionListener := mainArena.allianceSelectionNotifier.Listen()
defer close(allianceSelectionListener)
lowerThirdListener := mainArena.lowerThirdNotifier.Listen()
defer close(lowerThirdListener)
// Send the various notifications immediately upon connection.
var data interface{}
@@ -186,6 +188,12 @@ func AudienceDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) {
}
messageType = "allianceSelection"
message = cachedAlliances
case lowerThird, ok := <-lowerThirdListener:
if !ok {
return
}
messageType = "lowerThird"
message = lowerThird
}
err = websocket.Write(messageType, message)
if err != nil {

46
lower_third.go Normal file
View File

@@ -0,0 +1,46 @@
// Copyright 2014 Team 254. All Rights Reserved.
// Author: pat@patfairbank.com (Patrick Fairbank)
//
// Model and datastore CRUD methods for the text on a lower third slide.
package main
type LowerThird struct {
Id int
TopText string
BottomText string
}
func (database *Database) CreateLowerThird(lowerThird *LowerThird) error {
return database.lowerThirdMap.Insert(lowerThird)
}
func (database *Database) GetLowerThirdById(id int) (*LowerThird, error) {
lowerThird := new(LowerThird)
err := database.lowerThirdMap.Get(lowerThird, id)
if err != nil && err.Error() == "sql: no rows in result set" {
lowerThird = nil
err = nil
}
return lowerThird, err
}
func (database *Database) SaveLowerThird(lowerThird *LowerThird) error {
_, err := database.lowerThirdMap.Update(lowerThird)
return err
}
func (database *Database) DeleteLowerThird(lowerThird *LowerThird) error {
_, err := database.lowerThirdMap.Delete(lowerThird)
return err
}
func (database *Database) TruncateLowerThirds() error {
return database.lowerThirdMap.TruncateTables()
}
func (database *Database) GetAllLowerThirds() ([]LowerThird, error) {
var lowerThirds []LowerThird
err := database.teamMap.Select(&lowerThirds, "SELECT * FROM lower_thirds ORDER BY id")
return lowerThirds, err
}

78
setup_lower_thirds.go Normal file
View File

@@ -0,0 +1,78 @@
// Copyright 2014 Team 254. All Rights Reserved.
// Author: pat@patfairbank.com (Patrick Fairbank)
//
// Web routes for managing lower thirds.
package main
import (
"html/template"
"net/http"
"strconv"
)
// Shows the lower third configuration page.
func LowerThirdsGetHandler(w http.ResponseWriter, r *http.Request) {
template, err := template.ParseFiles("templates/lower_thirds.html", "templates/base.html")
if err != nil {
handleWebErr(w, err)
return
}
lowerThirds, err := db.GetAllLowerThirds()
if err != nil {
handleWebErr(w, err)
return
}
data := struct {
*EventSettings
LowerThirds []LowerThird
}{eventSettings, lowerThirds}
err = template.ExecuteTemplate(w, "base", data)
if err != nil {
handleWebErr(w, err)
return
}
}
// Saves the new or modified lower third to the database and triggers showing it on the audience display.
func LowerThirdsPostHandler(w http.ResponseWriter, r *http.Request) {
lowerThirdId, _ := strconv.Atoi(r.PostFormValue("id"))
lowerThird, err := db.GetLowerThirdById(lowerThirdId)
if err != nil {
handleWebErr(w, err)
return
}
if r.PostFormValue("action") == "delete" {
err := db.DeleteLowerThird(lowerThird)
if err != nil {
handleWebErr(w, err)
return
}
} else {
// Save the lower third even if the show or hide buttons were clicked.
if lowerThird == nil {
lowerThird = &LowerThird{TopText: r.PostFormValue("topText"),
BottomText: r.PostFormValue("bottomText")}
err = db.CreateLowerThird(lowerThird)
} else {
lowerThird.TopText = r.PostFormValue("topText")
lowerThird.BottomText = r.PostFormValue("bottomText")
err = db.SaveLowerThird(lowerThird)
}
if err != nil {
handleWebErr(w, err)
return
}
if r.PostFormValue("action") == "show" {
mainArena.lowerThirdNotifier.Notify(lowerThird)
mainArena.audienceDisplayScreen = "lowerThird"
mainArena.audienceDisplayNotifier.Notify(nil)
} else if r.PostFormValue("action") == "hide" {
mainArena.audienceDisplayScreen = "blank"
mainArena.audienceDisplayNotifier.Notify(nil)
}
}
http.Redirect(w, r, "/setup/lower_thirds", 302)
}

View File

@@ -304,3 +304,33 @@ html {
width: 250px;
color: #fff;
}
#lowerThird {
display: none;
position: absolute;
left: -1000px;
bottom: 100px;
background-color: #fff;
border: 1px solid #000;
color: #000;
font-size: 30px;
width: 800px;
height: 87px;
}
#lowerThirdLogo {
margin: 20px;
height: 45px;
float: left;
}
#lowerThirdTop {
display: none;
font-family: "FuturaLTBold";
}
#lowerThirdBottom {
display: none;
font-family: "FuturaLT";
}
#lowerThirdSingle {
display: none;
font-family: "FuturaLTBold";
line-height: 87px;
}

View File

@@ -88,3 +88,6 @@
.scoring-message {
color: #f00;
}
.btn-lower-third {
width: 80px;
}

View File

@@ -97,6 +97,21 @@ var handleAllianceSelection = function(alliances) {
}
};
var handleLowerThird = function(data) {
if (data.BottomText == "") {
$("#lowerThirdTop").hide();
$("#lowerThirdBottom").hide();
$("#lowerThirdSingle").text(data.TopText);
$("#lowerThirdSingle").show();
} else {
$("#lowerThirdSingle").hide();
$("#lowerThirdTop").text(data.TopText);
$("#lowerThirdBottom").text(data.BottomText);
$("#lowerThirdTop").show();
$("#lowerThirdBottom").show();
}
};
var transitionBlankToIntro = function(callback) {
$("#centering").transition({queue: false, bottom: "0px"}, 500, "ease", function() {
$(".teams").transition({queue: false, width: "75px"}, 100, "linear", function() {
@@ -233,6 +248,20 @@ var transitionAllianceSelectionToBlank = function(callback) {
}
};
var transitionBlankToLowerThird = function(callback) {
$("#lowerThird").show();
$("#lowerThird").transition({queue: false, left: "150px"}, 750, "ease", callback);
};
var transitionLowerThirdToBlank = function(callback) {
$("#lowerThird").transition({queue: false, left: "-1000px"}, 1000, "ease", function() {
$("#lowerThird").hide();
if (callback) {
callback();
}
});
};
$(function() {
// Set up the websocket back to the server.
websocket = new CheesyWebsocket("/displays/audience/websocket", {
@@ -243,7 +272,8 @@ $(function() {
realtimeScore: function(event) { handleRealtimeScore(event.data); },
setFinalScore: function(event) { handleSetFinalScore(event.data); },
playSound: function(event) { handlePlaySound(event.data); },
allianceSelection: function(event) { handleAllianceSelection(event.data); }
allianceSelection: function(event) { handleAllianceSelection(event.data); },
lowerThird: function(event) { handleLowerThird(event.data); }
});
// Map how to transition from one screen to another. Missing links between screens indicate that first we
@@ -254,7 +284,8 @@ $(function() {
match: transitionBlankToInMatch,
score: transitionBlankToScore,
logo: transitionBlankToLogo,
allianceSelection: transitionBlankToAllianceSelection
allianceSelection: transitionBlankToAllianceSelection,
lowerThird: transitionBlankToLowerThird
},
intro: {
blank: transitionIntroToBlank,
@@ -274,6 +305,9 @@ $(function() {
},
allianceSelection: {
blank: transitionAllianceSelectionToBlank
},
lowerThird: {
blank: transitionLowerThirdToBlank
}
}
});

View File

@@ -112,6 +112,7 @@ var handleMatchTime = function(data) {
};
var handleSetAudienceDisplay = function(data) {
$("input[name=audienceDisplay]:checked").prop("checked", false);
$("input[name=audienceDisplay][value=" + data + "]").prop("checked", true);
};
@@ -123,6 +124,7 @@ var handleScoringStatus = function(data) {
};
var handleSetAllianceStationDisplay = function(data) {
$("input[name=allianceStationDisplay]:checked").prop("checked", false);
$("input[name=allianceStationDisplay][value=" + data + "]").prop("checked", true);
};

View File

@@ -50,7 +50,7 @@
</div>
</div>
<div class="text-center" id="matchCircle">
<img id="logo" src="/static/img/logo-min.svg"</img>
<img id="logo" src="/static/img/logo-min.svg" />
<div id="matchTime"></div>
</div>
</div>
@@ -59,7 +59,7 @@
<div class="blinds right center-blank"></div>
<div class="blinds left background"></div>
<div id="blindsCenter">
<img id="blindsLogo" src="/static/img/logo-min.svg"</img>
<img id="blindsLogo" src="/static/img/logo-min.svg" />
</div>
<div class="blinds left center-blank"></div>
<div id="finalScore">
@@ -101,6 +101,12 @@
<div id="allianceSelectionCentering" style="display: none;">
<div id="allianceSelection"></div>
</div>
<div id="lowerThird">
<img id="lowerThirdLogo" src="/static/img/logo-min.svg" />
<div id="lowerThirdTop"></div>
<div id="lowerThirdBottom"></div>
<div id="lowerThirdSingle"></div>
</div>
<script id="allianceSelectionTemplate" type="text/x-handlebars-template">
<table id="allianceSelectionTable">
{{"{{#each this}}"}}

View File

@@ -25,6 +25,7 @@
<li><a href="/setup/teams">Team List</a></li>
<li><a href="/setup/schedule">Match Scheduling</a></li>
<li><a href="/setup/alliance_selection">Alliance Selection</a></li>
<li><a href="/setup/lower_thirds">Lower Thirds</a></li>
</ul>
</li>
<li class="dropdown">

View File

@@ -0,0 +1,43 @@
{{define "title"}}Lower Thirds{{end}}
{{define "body"}}
<div class="row">
<div class="col-lg-6 col-lg-offset-3">
<div class="well">
<legend>Lower Thirds</legend>
{{range $lowerThird := .LowerThirds}}
<form class="form-horizontal" action="/setup/lower_thirds" method="POST">
<div class="form-group">
<div class="col-lg-7">
<input type="hidden" name="id" value="{{$lowerThird.Id}}" />
<input type="text" class="form-control" name="topText" value="{{$lowerThird.TopText}}"
placeholder="Top Text"/>
<input type="text" class="form-control" name="bottomText" value="{{$lowerThird.BottomText}}"
placeholder="Bottom Text"/>
</div>
<div class="col-lg-5">
<button type="submit" class="btn btn-info btn-lower-third" name="action" value="save">Save</button>
<button type="submit" class="btn btn-success btn-lower-third" name="action" value="show">Show</button>
<br />
<button type="submit" class="btn btn-primary btn-lower-third" name="action" value="delete">Delete</button>
<button type="submit" class="btn btn-default btn-lower-third" name="action" value="hide">Hide</button>
</div>
</div>
</form>
{{end}}
<form class="form-horizontal" action="/setup/lower_thirds" method="POST">
<div class="form-group">
<div class="col-lg-7">
<input type="text" class="form-control" name="topText" placeholder="Top or Solo Text" />
<input type="text" class="form-control" name="bottomText" placeholder="Bottom Text" />
</div>
<div class="col-lg-5">
<button type="submit" class="btn btn-info btn-lower-third" name="action" value="save">Save</button>
</div>
</div>
</form>
</div>
</div>
</div>
{{end}}
{{define "script"}}
{{end}}

2
web.go
View File

@@ -122,6 +122,8 @@ func newHandler() http.Handler {
router.HandleFunc("/setup/alliance_selection/finalize", AllianceSelectionFinalizeHandler).Methods("POST")
router.HandleFunc("/setup/field", FieldGetHandler).Methods("GET")
router.HandleFunc("/setup/field", FieldPostHandler).Methods("POST")
router.HandleFunc("/setup/lower_thirds", LowerThirdsGetHandler).Methods("GET")
router.HandleFunc("/setup/lower_thirds", LowerThirdsPostHandler).Methods("POST")
router.HandleFunc("/match_play", MatchPlayHandler).Methods("GET")
router.HandleFunc("/match_play/{matchId}/load", MatchPlayLoadHandler).Methods("GET")
router.HandleFunc("/match_play/{matchId}/show_result", MatchPlayShowResultHandler).Methods("GET")