Initial commit

This commit is contained in:
Trey t
2026-03-26 15:37:31 -05:00
commit bae265b132
41 changed files with 8596 additions and 0 deletions

200
mlbTVOS/Models/Game.swift Normal file
View 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
}
}

View File

@@ -0,0 +1,62 @@
import SwiftUI
enum TeamAssets {
// MLB team ID mapping for logo URLs
static let teamIds: [String: Int] = [
"ARI": 109, "AZ": 109, "ATL": 144, "BAL": 110, "BOS": 111,
"CHC": 112, "CWS": 145, "CIN": 113, "CLE": 114, "COL": 115,
"DET": 116, "HOU": 117, "KC": 118, "LAA": 108, "LAD": 119,
"MIA": 146, "MIL": 158, "MIN": 142, "NYM": 121, "NYY": 147,
"OAK": 133, "ATH": 133, "PHI": 143, "PIT": 134, "SD": 135,
"SF": 137, "SEA": 136, "STL": 138, "TB": 139, "TEX": 140,
"TOR": 141, "WSH": 120,
]
// Primary team colors
static let teamColors: [String: Color] = [
"ARI": Color(red: 0.65, green: 0.10, blue: 0.19),
"ATL": Color(red: 0.81, green: 0.07, blue: 0.26),
"BAL": Color(red: 0.87, green: 0.31, blue: 0.07),
"BOS": Color(red: 0.74, green: 0.09, blue: 0.13),
"CHC": Color(red: 0.00, green: 0.19, blue: 0.56),
"CWS": Color(red: 0.15, green: 0.15, blue: 0.15),
"CIN": Color(red: 0.77, green: 0.06, blue: 0.15),
"CLE": Color(red: 0.00, green: 0.17, blue: 0.38),
"COL": Color(red: 0.20, green: 0.11, blue: 0.35),
"DET": Color(red: 0.00, green: 0.18, blue: 0.42),
"HOU": Color(red: 0.00, green: 0.18, blue: 0.39),
"KC": Color(red: 0.00, green: 0.22, blue: 0.53),
"LAA": Color(red: 0.73, green: 0.07, blue: 0.15),
"LAD": Color(red: 0.00, green: 0.22, blue: 0.56),
"MIA": Color(red: 0.00, green: 0.63, blue: 0.79),
"MIL": Color(red: 0.07, green: 0.15, blue: 0.34),
"MIN": Color(red: 0.00, green: 0.17, blue: 0.38),
"NYM": Color(red: 0.00, green: 0.18, blue: 0.48),
"NYY": Color(red: 0.00, green: 0.14, blue: 0.30),
"OAK": Color(red: 0.00, green: 0.30, blue: 0.18),
"ATH": Color(red: 0.00, green: 0.30, blue: 0.18),
"PHI": Color(red: 0.76, green: 0.08, blue: 0.18),
"PIT": Color(red: 0.99, green: 0.73, blue: 0.01),
"SD": Color(red: 0.18, green: 0.12, blue: 0.07),
"SF": Color(red: 0.99, green: 0.31, blue: 0.07),
"SEA": Color(red: 0.00, green: 0.22, blue: 0.36),
"STL": Color(red: 0.76, green: 0.10, blue: 0.15),
"TB": Color(red: 0.00, green: 0.18, blue: 0.46),
"TEX": Color(red: 0.00, green: 0.21, blue: 0.52),
"TOR": Color(red: 0.08, green: 0.25, blue: 0.52),
"WSH": Color(red: 0.67, green: 0.09, blue: 0.19),
]
static func color(for code: String) -> Color {
teamColors[code.uppercased()] ?? .gray
}
static func logoURL(for code: String) -> URL? {
guard let id = teamIds[code.uppercased()] else { return nil }
return URL(string: "https://midfield.mlbstatic.com/v1/team/\(id)/spots/72")
}
static func logoURL(forId id: Int) -> URL {
URL(string: "https://midfield.mlbstatic.com/v1/team/\(id)/spots/72")!
}
}