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
|
||||
}
|
||||
}
|
||||
62
mlbTVOS/Models/TeamAssets.swift
Normal file
62
mlbTVOS/Models/TeamAssets.swift
Normal 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")!
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user