Add game center, per-model shuffle, audio focus fixes, README, tests

- README.md with build/architecture overview
- Game Center screen with at-bat timeline, pitch sequence, spray chart,
  and strike zone component views
- VideoShuffle service: per-model bucketed random selection with
  no-back-to-back guarantee; replaces flat shuffle-bag approach
- Refresh JWT token for authenticated NSFW feed; add josie-hamming-2
  and dani-speegle-2 to the user list
- MultiStreamView audio focus: remove redundant isMuted writes during
  startStream and playNextWerkoutClip so audio stops ducking during
  clip transitions; gate AVAudioSession.setCategory(.playback) behind
  a one-shot flag
- GamesViewModel.attachPlayer: skip mute recalculation when the same
  player is re-attached (prevents toggle flicker on item replace)
- mlbTVOSTests target wired through project.yml with
  GENERATE_INFOPLIST_FILE; VideoShuffleTests covers groupByModel,
  pickRandomFromBuckets, real-distribution no-back-to-back invariant,
  and uniform model distribution over 6000 picks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-04-11 11:02:46 -05:00
parent 58e4c36963
commit 88308b46f5
17 changed files with 2069 additions and 313 deletions

91
README.md Normal file
View File

@@ -0,0 +1,91 @@
# 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](https://github.com/yonaskolb/XcodeGen). After editing
`project.yml` or adding/removing source files, run:
```sh
xcodegen generate
```
## Building
Requires Xcode 26.3+ and tvOS/iOS 18.0 SDKs.
```sh
# 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`](https://gitea.treytartt.com) 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`.