Files
MLBApp/mlbTVOS/Models/Game.swift
2026-03-26 15:37:31 -05:00

201 lines
5.2 KiB
Swift

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
}
}