mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 13:46:44 -04:00
Add avatars to match intro and final score audience views.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,3 +27,4 @@ _testmain.go
|
||||
*.out
|
||||
.DS_Store
|
||||
static/logs
|
||||
static/img/avatars
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
BIN
static/img/avatars/0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 B |
@@ -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); },
|
||||
|
||||
@@ -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"> </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">
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user