Trey t f59df9b04d Fix hero card content clipping: flex height, simpler team rows, smaller title
Title was clipping off the left edge ("ston Astros" instead of "Houston
Astros"). Root cause was too much content in a fixed-height card.

Fixed by: using minHeight instead of fixed height so the card expands
to fit content, simplified team rows to just logo + code + record +
score (removed full team name and standings summary since the title
already shows them), reduced title from 44pt to 36pt, added .clipped()
to hero image, added .fixedSize(horizontal: false, vertical: true).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 17:01:58 -05:00
2026-03-26 15:37:31 -05:00

mlbTVOS

Personal MLB streaming app for Apple TV and iPhone/iPad, built with SwiftUI. Streams live and archived MLB games, plus a few non-MLB channels (MLB Network, an authenticated private feed).

Targets

  • mlbTVOS — tvOS application (primary)
  • mlbIOS — iOS/iPadOS application (shares most sources with the tvOS target)
  • mlbTVOSTests — Swift Testing unit tests

The Xcode project is generated from project.yml via XcodeGen. After editing project.yml or adding/removing source files, run:

xcodegen generate

Building

Requires Xcode 26.3+ and tvOS/iOS 18.0 SDKs.

# tvOS
xcodebuild build -project mlbTVOS.xcodeproj -scheme mlbTVOS \
  -destination 'platform=tvOS Simulator,name=Apple TV 4K (3rd generation)'

# Tests
xcodebuild test -project mlbTVOS.xcodeproj -scheme mlbTVOSTests \
  -destination 'platform=tvOS Simulator,name=Apple TV 4K (3rd generation)'

Features

Dashboard

Today's live/scheduled/final games, standings ticker, and special channel entry points. Tapping a game opens the Game Center with at-bat timeline, pitch sequence, spray chart, and strike zone overlays.

Multi-stream view

Up to four simultaneous streams in an adaptive grid. Only one stream holds audio focus at a time; tap any tile to transfer focus. Streams can be single broadcasts, team-filtered feeds, or special channels.

Single-stream player

Full-screen AVPlayer with HLS adaptive streaming, custom HTTP headers, and automatic retry on failure.

Special playback channels

  • MLB Network — 24/7 channel via the public MLB stream
  • Werkout NSFW — authenticated private feed served by ofapp on the home server; uses a JWT cookie and rotates clips through a per-model random shuffle

Video shuffle (per-model random)

VideoShuffle groups clips by source folder (e.g. which model/creator the video came from) and picks randomly one bucket at a time, excluding the previously-played bucket. This guarantees no back-to-back same source and produces a roughly uniform distribution across sources even when one source dominates the raw data by a large factor. See mlbTVOS/Services/VideoShuffle.swift and mlbTVOSTests/VideoShuffleTests.swift for the pure functions and their test coverage.

Architecture

  • mlbTVOS/mlbTVOSApp.swift — app entry point, creates a single GamesViewModel injected into the environment
  • mlbTVOS/ViewModels/GamesViewModel.swift — observable state store for games, standings, active streams, audio focus, and video shuffle bags; fetches from MLB Stats API and the auxiliary stream service
  • mlbTVOS/ViewModels/GameCenterViewModel.swift — per-game live data (at-bats, pitches, box score) used by the Game Center screen
  • mlbTVOS/Services/ — API clients (MLBStatsAPI, MLBServerAPI) and pure helpers (VideoShuffle)
  • mlbTVOS/Views/ — SwiftUI screens and component library (Components/AtBatTimelineView, PitchSequenceView, SprayChartView, StrikeZoneView, etc.)

All new Swift code uses Swift 6 strict concurrency (SWIFT_STRICT_CONCURRENCY: complete).

Configuration

No secret management is set up. The authenticated feed cookie is hardcoded in mlbTVOS/Views/DashboardView.swift (SpecialPlaybackChannelConfig.werkoutNSFWCookie) and must be refreshed manually when the JWT expires — generate a new one from the ofapp server's jwt_secret using the same userId and a longer exp.

Description
No description provided
Readme 473 KiB
Languages
Swift 100%