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