mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-02 12:08:37 +01:00
docs(casting): add casting-player split design spec
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
# Chromecast Player Split — Design
|
||||
|
||||
**Date:** 2026-05-22
|
||||
**Branch:** `refactor-chromecast` (PR #1402)
|
||||
**Sub-project:** C of the Chromecast refactor
|
||||
**Status:** Approved design — pending implementation plan
|
||||
|
||||
---
|
||||
|
||||
## 1. Problem
|
||||
|
||||
`app/(auth)/casting-player.tsx` is a 1428-line god-component. One file owns: cast SDK
|
||||
hooks, item fetching, live-progress tracking, slider/scrubbing, trickplay, track
|
||||
selection, episode/season fetching, segment skipping, the dismiss gesture, and ~780
|
||||
lines of JSX. It is hard to read, hard to change safely, and every sub-project so far
|
||||
has had to navigate it.
|
||||
|
||||
## 2. Scope
|
||||
|
||||
**In scope:** a purely structural decomposition of `casting-player.tsx` into 4 custom
|
||||
hooks, 6 presentational components, and a thin orchestrator.
|
||||
|
||||
**Out of scope — and a hard constraint:** **zero behaviour change.** This is a
|
||||
mechanical extraction. No bug fixes, no feature changes, no logic edits. Known issues
|
||||
in this code (the `loadEpisode` / `currentItem` race from sub-project A's review; the
|
||||
trickplay window truncation, the progress-bar touch overlap, the time-label position)
|
||||
are explicitly NOT touched here — they belong to a later UX sub-project, and are
|
||||
easier to fix once the code lives in small focused files.
|
||||
|
||||
## 3. Architecture
|
||||
|
||||
`casting-player.tsx` becomes an orchestrator (~150-200 lines): it calls the hooks,
|
||||
assembles the data, and renders the 6 components plus the 3 existing modal components.
|
||||
|
||||
State flows orchestrator → components by **props** (approved approach 1) — no React
|
||||
context. Presentational components take typed props; all logic lives in hooks.
|
||||
|
||||
## 4. Hooks (`hooks/`)
|
||||
|
||||
| Hook | Extracts | Returns (shape) |
|
||||
|---|---|---|
|
||||
| `useCastPlayerItem` | `fetchedItem` state, its fetch effect, the `currentItem` derivation | `{ fetchedItem, currentItem }` |
|
||||
| `useCastPlayerProgress` | slider shared values, `isScrubbing`, `trickplayTime`, `scrubPercentage`, `liveProgress`, the sync refs/effects, the `useTrickplay` call | slider state, scrub handlers, `progress`, trickplay data |
|
||||
| `useCastEpisodes` | `episodes` / `nextEpisode` / `seasonData` state, their fetch effects, `loadEpisode` | `{ episodes, nextEpisode, seasonData, loadEpisode }` |
|
||||
| `useCastDismissGesture` | `translateY`, `context`, `panGesture`, `dismissModal`, `animatedStyle` | `{ panGesture, animatedStyle, dismissModal }` |
|
||||
|
||||
Existing hooks are reused unchanged: `useCasting`, `useCastSelection`,
|
||||
`useChromecastSegments`, `useTrickplay` (the latter called inside
|
||||
`useCastPlayerProgress`).
|
||||
|
||||
`useCastPlayerProgress` is the most intricate hook (shared values, refs, the
|
||||
live-progress interpolation) — it must be extracted with care and reviewed closely.
|
||||
|
||||
## 5. Components (`components/casting/player/`)
|
||||
|
||||
| Component | Extracts (JSX section) |
|
||||
|---|---|
|
||||
| `CastPlayerHeader` | dismiss chevron, connection indicator, settings button |
|
||||
| `CastPlayerTitle` | title + episode/season info |
|
||||
| `CastPlayerPoster` | poster image, buffering overlay, skip intro/credits bar |
|
||||
| `CastPlayerEpisodeControls` | the 4-button row (Episodes / Previous / Next / Stop) |
|
||||
| `CastPlayerProgressBar` | slider, trickplay preview, time display |
|
||||
| `CastPlayerTransportControls` | rewind / play-pause / forward |
|
||||
|
||||
Each is a pure presentational component with typed props. The 3 modal components
|
||||
(`ChromecastDeviceSheet`, `ChromecastEpisodeList`, `ChromecastSettingsMenu`) already
|
||||
exist and stay as-is — the orchestrator keeps rendering them.
|
||||
|
||||
## 6. Constraints
|
||||
|
||||
- **Mechanical extraction only.** Each component's / hook's props and returns mirror
|
||||
exactly what the inline code used. No new logic, no renamed behaviour, no fixes.
|
||||
- Component prop interfaces are derived from what the extracted JSX references in the
|
||||
original component scope.
|
||||
- Follow existing repo conventions: hooks flat in `hooks/`; the new components grouped
|
||||
under `components/casting/player/`.
|
||||
|
||||
## 7. Verification
|
||||
|
||||
The casting UI has no unit tests (consistent with the rest of the casting UI — it is
|
||||
React Native / SDK-heavy). Verification is:
|
||||
|
||||
- `bun run typecheck` — green after every task (catches wiring/prop errors).
|
||||
- `bun test utils/casting/` — stays green (the pure-logic suites are untouched).
|
||||
- A full **manual re-test** of the cast player after the split: cast a movie and an
|
||||
episode, switch audio / subtitle / quality / version, navigate episodes, scrub the
|
||||
progress bar, trigger trickplay, dismiss the player. Behaviour must be identical to
|
||||
before the split.
|
||||
|
||||
## 8. Files
|
||||
|
||||
**Created:**
|
||||
- `hooks/useCastPlayerItem.ts`, `hooks/useCastPlayerProgress.ts`,
|
||||
`hooks/useCastEpisodes.ts`, `hooks/useCastDismissGesture.ts`
|
||||
- `components/casting/player/CastPlayerHeader.tsx`, `CastPlayerTitle.tsx`,
|
||||
`CastPlayerPoster.tsx`, `CastPlayerEpisodeControls.tsx`,
|
||||
`CastPlayerProgressBar.tsx`, `CastPlayerTransportControls.tsx`
|
||||
|
||||
**Modified:**
|
||||
- `app/(auth)/casting-player.tsx` → thin orchestrator.
|
||||
|
||||
## 9. Success criteria
|
||||
|
||||
- `casting-player.tsx` is ~150-200 lines and contains no inline logic clusters or
|
||||
large JSX sections — only hook calls and component composition.
|
||||
- Each new file has one clear responsibility and a typed interface.
|
||||
- `bun run typecheck` and `bun test utils/casting/` pass.
|
||||
- The manual re-test shows behaviour identical to before the split.
|
||||
|
||||
## 10. Risks
|
||||
|
||||
- The hooks share state (progress feeds the slider, the time display, and segment
|
||||
skipping). The extraction must preserve the exact data flow and effect ordering.
|
||||
Mitigation: one hook / component per task, `bun run typecheck` after each, and a
|
||||
full manual re-test at the end.
|
||||
- `useCastPlayerProgress` carries reanimated shared values and timing refs — the
|
||||
highest-risk extraction; its task gets the closest review.
|
||||
- No unit tests guard the UI — the manual re-test is the only behavioural safety net.
|
||||
Reference in New Issue
Block a user