Add avatars to match intro and final score audience views.

This commit is contained in:
Patrick Fairbank
2018-09-18 00:36:25 -07:00
parent 7297dc4146
commit c9e7640a28
7 changed files with 117 additions and 10 deletions

1
.gitignore vendored
View File

@@ -27,3 +27,4 @@ _testmain.go
*.out
.DS_Store
static/logs
static/img/avatars

View File

@@ -8,6 +8,7 @@ package partner
import (
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/Team254/cheesy-arena/game"
@@ -20,6 +21,7 @@ import (
const (
tbaBaseUrl = "https://www.thebluealliance.com"
tbaAuthKey = "MAApv9MCuKY9MSFkXLuzTSYBCdosboxDq8Q3ujUE2Mn8PD3Nmv64uczu5Lvy0NQ3"
avatarsDir = "static/img/avatars"
)
type TbaClient struct {
@@ -119,6 +121,11 @@ type TbaEvent struct {
Name string `json:"name"`
}
type TbaMediaItem struct {
Details map[string]interface{} `json:"details"`
Type string `json:"type"`
}
func NewTbaClient(eventCode, secretId, secret string) *TbaClient {
return &TbaClient{BaseUrl: tbaBaseUrl, eventCode: eventCode, secretId: secretId, secret: secret,
eventNamesCache: make(map[string]string)}
@@ -204,6 +211,47 @@ func (client *TbaClient) GetTeamAwards(teamNumber int) ([]*TbaAward, error) {
return awards, nil
}
func (client *TbaClient) DownloadTeamAvatar(teamNumber, year int) error {
path := fmt.Sprintf("/api/v3/team/%s/media/%d", getTbaTeam(teamNumber), year)
resp, err := client.getRequest(path)
if err != nil {
return err
}
// Get the response and handle errors
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var mediaItems []*TbaMediaItem
err = json.Unmarshal(body, &mediaItems)
if err != nil {
return err
}
for _, item := range mediaItems {
if item.Type == "avatar" {
base64String, ok := item.Details["base64Image"].(string)
if !ok {
return fmt.Errorf("Could not interpret avatar response from TBA: %v", item)
}
avatarBytes, err := base64.StdEncoding.DecodeString(base64String)
if err != nil {
return err
}
// Store the avatar to disk as a PNG file.
avatarPath := fmt.Sprintf("%s/%d.png", avatarsDir, teamNumber)
ioutil.WriteFile(avatarPath, avatarBytes, 0644)
return nil
}
}
return fmt.Errorf("No avatar found for team %d in year %d.", teamNumber, year)
}
// Uploads the event team list to The Blue Alliance.
func (client *TbaClient) PublishTeams(database *model.Database) error {
teams, err := database.GetAllTeams()

View File

@@ -33,6 +33,13 @@ html {
display: table;
font-family: "FuturaLT";
}
.avatars {
display: none;
}
.avatar {
height: 25px;
margin: 4px 10px 3px;
}
.valign-cell {
display: table-cell;
vertical-align: middle;
@@ -257,7 +264,7 @@ html {
}
#finalScore {
position: fixed;
width: 800px;
width: 950px;
height: 550px;
top: 65px;
bottom: 0;
@@ -296,15 +303,24 @@ html {
text-align: center;
color: #fff;
font-family: "FuturaLT";
font-size: 35px;
font-size: 32px;
}
.final-teams span {
margin: 0px 20px;
margin: 0 10px;
}
.final-avatar {
height: 35px;
position: relative;
bottom: 5px;
}
#leftFinalTeams {
padding-right: 5%;
clear: left;
border-right: 2px solid #333;
}
#rightFinalTeams {
padding-left: 5%;
}
.final-breakdown {
float: left;
width: 33%;

BIN
static/img/avatars/0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

View File

@@ -40,9 +40,15 @@ var handleMatchLoad = function(data) {
$("#" + redSide + "Team1").text(data.Match.Red1);
$("#" + redSide + "Team2").text(data.Match.Red2);
$("#" + redSide + "Team3").text(data.Match.Red3);
$("#" + redSide + "Team1Avatar").attr("src", getAvatarUrl(data.Match.Red1));
$("#" + redSide + "Team2Avatar").attr("src", getAvatarUrl(data.Match.Red2));
$("#" + redSide + "Team3Avatar").attr("src", getAvatarUrl(data.Match.Red3));
$("#" + blueSide + "Team1").text(data.Match.Blue1);
$("#" + blueSide + "Team2").text(data.Match.Blue2);
$("#" + blueSide + "Team3").text(data.Match.Blue3);
$("#" + blueSide + "Team1Avatar").attr("src", getAvatarUrl(data.Match.Blue1));
$("#" + blueSide + "Team2Avatar").attr("src", getAvatarUrl(data.Match.Blue2));
$("#" + blueSide + "Team3Avatar").attr("src", getAvatarUrl(data.Match.Blue3));
$("#matchName").text(data.MatchType + " " + data.Match.DisplayName);
};
@@ -100,6 +106,9 @@ var handleScorePosted = function(data) {
$("#" + redSide + "FinalTeam1").text(data.Match.Red1);
$("#" + redSide + "FinalTeam2").text(data.Match.Red2);
$("#" + redSide + "FinalTeam3").text(data.Match.Red3);
$("#" + redSide + "FinalTeam1Avatar").attr("src", getAvatarUrl(data.Match.Red1));
$("#" + redSide + "FinalTeam2Avatar").attr("src", getAvatarUrl(data.Match.Red2));
$("#" + redSide + "FinalTeam3Avatar").attr("src", getAvatarUrl(data.Match.Red3));
$("#" + redSide + "FinalAutoRunPoints").text(data.RedScoreSummary.AutoRunPoints);
$("#" + redSide + "FinalOwnershipPoints").text(data.RedScoreSummary.OwnershipPoints);
$("#" + redSide + "FinalVaultPoints").text(data.RedScoreSummary.VaultPoints);
@@ -113,6 +122,9 @@ var handleScorePosted = function(data) {
$("#" + blueSide + "FinalTeam1").text(data.Match.Blue1);
$("#" + blueSide + "FinalTeam2").text(data.Match.Blue2);
$("#" + blueSide + "FinalTeam3").text(data.Match.Blue3);
$("#" + blueSide + "FinalTeam1Avatar").attr("src", getAvatarUrl(data.Match.Blue1));
$("#" + blueSide + "FinalTeam2Avatar").attr("src", getAvatarUrl(data.Match.Blue2));
$("#" + blueSide + "FinalTeam3Avatar").attr("src", getAvatarUrl(data.Match.Blue3));
$("#" + blueSide + "FinalAutoRunPoints").text(data.BlueScoreSummary.AutoRunPoints);
$("#" + blueSide + "FinalOwnershipPoints").text(data.BlueScoreSummary.OwnershipPoints);
$("#" + blueSide + "FinalVaultPoints").text(data.BlueScoreSummary.VaultPoints);
@@ -163,6 +175,8 @@ var handleLowerThird = function(data) {
};
var transitionBlankToIntro = function(callback) {
$(".avatars").show();
$(".avatars").css("opacity", 1);
$("#centering").transition({queue: false, bottom: "0px"}, 500, "ease", function() {
$(".teams").transition({queue: false, width: "65px"}, 100, "linear", function() {
$(".score").transition({queue: false, width: "120px"}, 500, "ease", function() {
@@ -175,6 +189,9 @@ var transitionBlankToIntro = function(callback) {
};
var transitionIntroToInMatch = function(callback) {
$(".avatars").transition({queue: false, opacity: 0}, 500, "ease", function() {
$(".avatars").hide();
});
$("#logo").transition({queue: false, top: "10px"}, 500, "ease");
$(".score").transition({queue: false, width: "275px"}, 500, "ease", function() {
$(".score-number").transition({queue: false, opacity: 1}, 750, "ease");
@@ -189,6 +206,8 @@ var transitionIntroToBlank = function(callback) {
$("#eventMatchInfo").hide();
$(".score").transition({queue: false, width: "0px"}, 500, "ease");
$(".teams").transition({queue: false, width: "40px"}, 500, "ease", function() {
$(".avatars").css("opacity", 0);
$(".avatars").hide();
$("#centering").transition({queue: false, bottom: "-340px"}, 1000, "ease", callback);
});
});
@@ -218,7 +237,10 @@ var transitionInMatchToIntro = function(callback) {
$("#matchTime").transition({queue: false, opacity: 0}, 300, "linear", function() {
$("#logo").transition({queue: false, top: "35px"}, 500, "ease");
$(".score").transition({queue: false, width: "120px"}, 500, "ease");
$(".teams").transition({queue: false, width: "65px"}, 500, "ease", callback);
$(".teams").transition({queue: false, width: "65px"}, 500, "ease", function() {
$(".avatars").show();
$(".avatars").transition({queue: false, opacity: 1}, 500, "ease", callback);
});
});
};
@@ -405,6 +427,10 @@ var initializeSponsorDisplay = function() {
});
};
var getAvatarUrl = function(teamId) {
return "/static/img/avatars/" + teamId + ".png";
};
$(function() {
// Read the configuration for this display from the URL query string.
var urlParams = new URLSearchParams(window.location.search);
@@ -420,6 +446,9 @@ $(function() {
$(".reversible-left").attr("data-reversed", reversed);
$(".reversible-right").attr("data-reversed", reversed);
// Fall back to a blank avatar if one doesn't exist for the team.
$(".avatar, .final-avatar").attr("onerror", "this.src='" + getAvatarUrl(0) + "';");
// Set up the websocket back to the server.
websocket = new CheesyWebsocket("/displays/audience/websocket", {
allianceSelection: function(event) { handleAllianceSelection(event.data); },

View File

@@ -24,6 +24,11 @@
</span>
</div>
<div class="score reversible-left" id="leftScore">
<div class="avatars">
<img class="avatar" id="leftTeam1Avatar" src="" /><br />
<img class="avatar" id="leftTeam2Avatar" src="" /><br />
<img class="avatar" id="leftTeam3Avatar" src="" />
</div>
<div class="score-fields score-fields-icons">
<span class="valign-cell">
<span class="power-up" id="leftForceCubesIcon">F</span><br />
@@ -44,6 +49,11 @@
<div class="score-number" id="leftScoreNumber">&nbsp;</div>
</div>
<div class="score reversible-right" id="rightScore">
<div class="avatars pull-right">
<img class="avatar" id="rightTeam1Avatar" src="" /><br />
<img class="avatar" id="rightTeam2Avatar" src="" /><br />
<img class="avatar" id="rightTeam3Avatar" src="" />
</div>
<div class="score-fields score-fields-icons pull-right">
<span class="valign-cell">
<span class="power-up" id="rightForceCubesIcon">F</span><br />
@@ -97,14 +107,14 @@
<div class="final-score reversible-left" id="leftFinalScore"></div>
<div class="final-score reversible-right" id="rightFinalScore"></div>
<div class="final-teams" id="leftFinalTeams">
<span id="leftFinalTeam1"></span>
<span id="leftFinalTeam2"></span>
<span id="leftFinalTeam3"></span>
<span id="leftFinalTeam1"></span><img class="final-avatar" id="leftFinalTeam1Avatar" src="" />
<span id="leftFinalTeam2"></span><img class="final-avatar" id="leftFinalTeam2Avatar" src="" />
<span id="leftFinalTeam3"></span><img class="final-avatar" id="leftFinalTeam3Avatar" src="" />
</div>
<div class="final-teams" id="rightFinalTeams">
<span id="rightFinalTeam1"></span>
<span id="rightFinalTeam2"></span>
<span id="rightFinalTeam3"></span>
<img class="final-avatar" id="rightFinalTeam1Avatar" src="" /><span id="rightFinalTeam1"></span>
<img class="final-avatar" id="rightFinalTeam2Avatar" src="" /><span id="rightFinalTeam2"></span>
<img class="final-avatar" id="rightFinalTeam3Avatar" src="" /><span id="rightFinalTeam3"></span>
</div>
<div class="final-breakdown" id="leftFinalBreakdown">
<span class="valign-cell">

View File

@@ -295,6 +295,9 @@ func (web *Web) getOfficialTeamInfo(teamId int) (*model.Team, error) {
}
}
// Download and store the team's avatar; if there isn't one, ignore the error.
web.arena.TbaClient.DownloadTeamAvatar(teamId, time.Now().Year())
// Use those variables to make a team object
team = model.Team{Id: teamId, Name: tbaTeam.Name, Nickname: tbaTeam.Nickname, City: tbaTeam.City,
StateProv: tbaTeam.StateProv, Country: tbaTeam.Country, RookieYear: tbaTeam.RookieYear,