Initial commit
This commit is contained in:
200
mlbTVOS/Models/Game.swift
Normal file
200
mlbTVOS/Models/Game.swift
Normal file
@@ -0,0 +1,200 @@
|
||||
import Foundation
|
||||
|
||||
struct Game: Identifiable, Sendable {
|
||||
let id: String
|
||||
let awayTeam: TeamInfo
|
||||
let homeTeam: TeamInfo
|
||||
let status: GameStatus
|
||||
let gameType: String?
|
||||
let startTime: String?
|
||||
let venue: String?
|
||||
let pitchers: String?
|
||||
let gamePk: String?
|
||||
let gameDate: String
|
||||
let broadcasts: [Broadcast]
|
||||
let isBlackedOut: Bool
|
||||
|
||||
// Rich data from Stats API
|
||||
var linescore: StatsLinescore?
|
||||
var currentInningDisplay: String?
|
||||
var awayPitcherId: Int?
|
||||
var homePitcherId: Int?
|
||||
|
||||
var awayPitcherHeadshotURL: URL? {
|
||||
guard let id = awayPitcherId else { return nil }
|
||||
return URL(string: "https://img.mlbstatic.com/mlb-photos/image/upload/w_213,q_auto:best/v1/people/\(id)/headshot/67/current")
|
||||
}
|
||||
|
||||
var homePitcherHeadshotURL: URL? {
|
||||
guard let id = homePitcherId else { return nil }
|
||||
return URL(string: "https://img.mlbstatic.com/mlb-photos/image/upload/w_213,q_auto:best/v1/people/\(id)/headshot/67/current")
|
||||
}
|
||||
|
||||
var displayTitle: String {
|
||||
"\(awayTeam.displayName) @ \(homeTeam.displayName)"
|
||||
}
|
||||
|
||||
var scoreDisplay: String? {
|
||||
guard let awayScore = awayTeam.score, let homeScore = homeTeam.score else {
|
||||
return nil
|
||||
}
|
||||
return "\(awayScore) - \(homeScore)"
|
||||
}
|
||||
|
||||
var hasStreams: Bool {
|
||||
!broadcasts.isEmpty && !isBlackedOut
|
||||
}
|
||||
|
||||
var isLive: Bool { status.isLive }
|
||||
var isFinal: Bool { if case .final_ = status { return true }; return false }
|
||||
}
|
||||
|
||||
struct TeamInfo: Sendable {
|
||||
let code: String
|
||||
let name: String
|
||||
let score: Int?
|
||||
var teamId: Int?
|
||||
var record: String?
|
||||
var divisionRank: String?
|
||||
var gamesBack: String?
|
||||
var streak: String?
|
||||
|
||||
var displayName: String {
|
||||
if !name.isEmpty { return name }
|
||||
return TeamDirectory.name(for: code)
|
||||
}
|
||||
|
||||
var logoURL: URL? {
|
||||
if let teamId { return TeamAssets.logoURL(forId: teamId) }
|
||||
return TeamAssets.logoURL(for: code)
|
||||
}
|
||||
|
||||
var standingSummary: String? {
|
||||
var parts: [String] = []
|
||||
if let rank = divisionRank {
|
||||
let suffix: String
|
||||
switch rank {
|
||||
case "1": suffix = "st"
|
||||
case "2": suffix = "nd"
|
||||
case "3": suffix = "rd"
|
||||
default: suffix = "th"
|
||||
}
|
||||
parts.append("\(rank)\(suffix)")
|
||||
}
|
||||
if let gb = gamesBack {
|
||||
parts.append("\(gb) GB")
|
||||
}
|
||||
if let streak {
|
||||
parts.append(streak)
|
||||
}
|
||||
return parts.isEmpty ? nil : parts.joined(separator: " · ")
|
||||
}
|
||||
}
|
||||
|
||||
struct Broadcast: Identifiable, Sendable {
|
||||
let id: String
|
||||
let teamCode: String
|
||||
let name: String
|
||||
let mediaId: String
|
||||
let streamURL: String
|
||||
|
||||
var displayLabel: String {
|
||||
"\(teamCode): \(name)"
|
||||
}
|
||||
}
|
||||
|
||||
enum GameStatus: Sendable {
|
||||
case scheduled(String)
|
||||
case live(String?)
|
||||
case final_
|
||||
case unknown
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .scheduled(let time): time
|
||||
case .live(let info): info ?? "Live"
|
||||
case .final_: "Final"
|
||||
case .unknown: ""
|
||||
}
|
||||
}
|
||||
|
||||
var isLive: Bool {
|
||||
if case .live = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
var isScheduled: Bool {
|
||||
if case .scheduled = self { return true }
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
struct StreamConfig: Sendable {
|
||||
let mediaId: String?
|
||||
let team: String?
|
||||
let resolution: String
|
||||
let date: String?
|
||||
let level: String?
|
||||
let skip: String?
|
||||
let audioTrack: String?
|
||||
let mediaType: String?
|
||||
let game: Int?
|
||||
|
||||
init(
|
||||
mediaId: String,
|
||||
resolution: String = "best",
|
||||
audioTrack: String? = nil
|
||||
) {
|
||||
self.mediaId = mediaId
|
||||
self.team = nil
|
||||
self.resolution = resolution
|
||||
self.date = nil
|
||||
self.level = nil
|
||||
self.skip = nil
|
||||
self.audioTrack = audioTrack
|
||||
self.mediaType = nil
|
||||
self.game = nil
|
||||
}
|
||||
|
||||
init(
|
||||
team: String,
|
||||
resolution: String = "best",
|
||||
date: String? = nil,
|
||||
level: String? = nil,
|
||||
skip: String? = nil,
|
||||
audioTrack: String? = nil,
|
||||
mediaType: String? = nil,
|
||||
game: Int? = nil
|
||||
) {
|
||||
self.mediaId = nil
|
||||
self.team = team
|
||||
self.resolution = resolution
|
||||
self.date = date
|
||||
self.level = level
|
||||
self.skip = skip
|
||||
self.audioTrack = audioTrack
|
||||
self.mediaType = mediaType
|
||||
self.game = game
|
||||
}
|
||||
}
|
||||
|
||||
enum TeamDirectory {
|
||||
static let teams: [String: String] = [
|
||||
"ARI": "D-backs", "AZ": "D-backs",
|
||||
"ATL": "Braves", "BAL": "Orioles", "BOS": "Red Sox",
|
||||
"CHC": "Cubs", "CWS": "White Sox", "CIN": "Reds",
|
||||
"CLE": "Guardians", "COL": "Rockies", "DET": "Tigers",
|
||||
"HOU": "Astros", "KC": "Royals", "LAA": "Angels",
|
||||
"LAD": "Dodgers", "MIA": "Marlins", "MIL": "Brewers",
|
||||
"MIN": "Twins", "NYM": "Mets", "NYY": "Yankees",
|
||||
"OAK": "Athletics", "ATH": "Athletics",
|
||||
"PHI": "Phillies", "PIT": "Pirates", "SD": "Padres",
|
||||
"SF": "Giants", "SEA": "Mariners", "STL": "Cardinals",
|
||||
"TB": "Rays", "TEX": "Rangers", "TOR": "Blue Jays",
|
||||
"WSH": "Nationals",
|
||||
]
|
||||
|
||||
static func name(for code: String) -> String {
|
||||
teams[code.uppercased()] ?? code
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user