1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
package espn
// These types mirror only the fields we consume from ESPN's public
// "site.api.espn.com" scoreboard JSON. Many fields are omitted on purpose;
// the payload is large and we map the rest away in client.go.
type scoreboard struct {
Events []event `json:"events"`
}
type event struct {
ID string `json:"id"`
Date string `json:"date"`
Name string `json:"name"`
ShortName string `json:"shortName"`
Status status `json:"status"`
Competitions []competition `json:"competitions"`
}
type competition struct {
Venue venue `json:"venue"`
Competitors []competitor `json:"competitors"`
Status status `json:"status"`
Headlines []headline `json:"headlines"`
Situation situationJSON `json:"situation"`
}
// situationJSON is the live play state ESPN ships on a baseball competition
// while in progress: the count, outs, occupied bases, and the current
// pitcher/batter (with one-line stat summaries).
type situationJSON struct {
Balls int `json:"balls"`
Strikes int `json:"strikes"`
Outs int `json:"outs"`
OnFirst bool `json:"onFirst"`
OnSecond bool `json:"onSecond"`
OnThird bool `json:"onThird"`
Pitcher playerSituation `json:"pitcher"`
Batter playerSituation `json:"batter"`
LastPlay lastPlayJSON `json:"lastPlay"`
}
type playerSituation struct {
Athlete athleteRef `json:"athlete"`
Summary string `json:"summary"`
}
type lastPlayJSON struct {
Text string `json:"text"`
}
type venue struct {
FullName string `json:"fullName"`
}
type headline struct {
ShortText string `json:"shortLinkText"`
Description string `json:"description"`
}
type competitor struct {
HomeAway string `json:"homeAway"`
Winner bool `json:"winner"`
Score string `json:"score"` // ESPN sends score as a string
Team teamJSON `json:"team"`
Records []record `json:"records"`
}
type record struct {
Type string `json:"type"`
Summary string `json:"summary"`
}
type teamJSON struct {
ID string `json:"id"`
Abbreviation string `json:"abbreviation"`
Name string `json:"name"`
ShortDisplayName string `json:"shortDisplayName"`
DisplayName string `json:"displayName"`
Color string `json:"color"`
AlternateColor string `json:"alternateColor"`
}
type status struct {
DisplayClock string `json:"displayClock"`
Period int `json:"period"`
Type statusType `json:"type"`
}
type statusType struct {
State string `json:"state"` // "pre" | "in" | "post"
Completed bool `json:"completed"`
Detail string `json:"detail"`
ShortDetail string `json:"shortDetail"`
}
// --- summary endpoint -------------------------------------------------------
// These mirror only the slices of the (large) summary payload we render in the
// detail view: soccer keyEvents and the per-team player box score.
type summary struct {
KeyEvents []keyEvent `json:"keyEvents"`
Plays []play `json:"plays"`
Boxscore boxscore `json:"boxscore"`
}
// play is one entry in the summary's play-by-play. We consume only scoring
// plays (baseball scoring timeline); the array is large so the rest is ignored.
type play struct {
Text string `json:"text"`
ScoringPlay bool `json:"scoringPlay"`
AwayScore int `json:"awayScore"`
HomeScore int `json:"homeScore"`
Period playPeriod `json:"period"`
Team eventTeam `json:"team"`
AlternativeType altType `json:"alternativeType"`
}
type playPeriod struct {
Type string `json:"type"` // "Top" | "Bottom"
Number int `json:"number"` // inning number
DisplayValue string `json:"displayValue"` // "4th Inning"
}
type altType struct {
Abbreviation string `json:"abbreviation"` // "HR"
Text string `json:"text"` // "Home Run"
}
type keyEvent struct {
Type eventType `json:"type"`
Text string `json:"text"`
ShortText string `json:"shortText"`
Period eventPeriod `json:"period"`
Clock eventClock `json:"clock"`
ScoringPlay bool `json:"scoringPlay"`
Team eventTeam `json:"team"`
Participants []participant `json:"participants"`
}
type eventType struct {
Text string `json:"text"`
Type string `json:"type"`
}
type eventPeriod struct {
Number int `json:"number"`
}
type eventClock struct {
DisplayValue string `json:"displayValue"`
}
type eventTeam struct {
ID string `json:"id"`
DisplayName string `json:"displayName"`
}
type participant struct {
Athlete athleteRef `json:"athlete"`
}
type athleteRef struct {
DisplayName string `json:"displayName"`
ShortName string `json:"shortName"`
}
type boxscore struct {
Players []playerBox `json:"players"`
Teams []teamStatBox `json:"teams"`
}
// teamStatBox is one team's flat team-level stat list from the soccer summary
// boxscore (possession, shots, corners…). homeAway pairs the two sides.
type teamStatBox struct {
HomeAway string `json:"homeAway"`
Statistics []teamStatJSON `json:"statistics"`
}
type teamStatJSON struct {
Name string `json:"name"` // "possessionPct", "totalShots"
Label string `json:"label"` // "Possession", "SHOTS"
DisplayValue string `json:"displayValue"` // "63.2", "11"
}
type playerBox struct {
Team teamJSON `json:"team"`
Statistics []statGroup `json:"statistics"`
}
type statGroup struct {
Name string `json:"name"`
Type string `json:"type"`
DisplayName string `json:"displayName"`
Labels []string `json:"labels"`
Athletes []athleteRow `json:"athletes"`
}
type athleteRow struct {
Athlete athleteRef `json:"athlete"`
Stats []string `json:"stats"`
}
|