mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-02 12:08:37 +01:00
docs(casting): add chromecast refactor handoff & resume document
Captures the full state of the A/B/C/D sub-projects and the #1367 prep: commit ranges, verification status, pending queue, key decisions, and how to resume the work in a later session.
This commit is contained in:
175
docs/superpowers/chromecast-refactor-handoff.md
Normal file
175
docs/superpowers/chromecast-refactor-handoff.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# Chromecast Refactor — Handoff & Resume Document
|
||||
|
||||
**Branch:** `refactor-chromecast` · **PR:** #1402 (draft) · **Last updated:** 2026-05-22
|
||||
|
||||
This document captures the full state of the Chromecast refactor so the work can be
|
||||
resumed in a later session. Specs and plans for each sub-project live in
|
||||
`docs/superpowers/specs/` and `docs/superpowers/plans/`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Summary
|
||||
|
||||
The Chromecast casting code (PR #1402) was refactored in four sub-projects plus one
|
||||
prep task. All four are **implemented, type-checked, and unit-tested**; the branch is
|
||||
86 commits ahead of `develop`, **46 commits unpushed** to `origin/refactor-chromecast`.
|
||||
|
||||
| Sub-project | What | Status |
|
||||
|---|---|---|
|
||||
| **A** — Device profiles | Per-device capability detection, profile builder, unified `loadCastMedia`, crash fixes (status 2100, 5.1, bitrate) | ✅ done, **verified on hardware** |
|
||||
| **Prep #1367** — Segment-skip | Backport segment-skip fixes to PR #1367, remove 177 dead lines | ✅ done, **pushed to #1367** |
|
||||
| **B** — Track switching | `CastSelection` source-of-truth via customData, audio/subtitle/quality/version switching, multi-version | ✅ done, **verified on hardware** |
|
||||
| **C** — Player split | `casting-player.tsx` 1428→574 lines: 6 components + 4 hooks | ✅ done, **needs manual re-test** |
|
||||
| **D** — Session & remote control | Correct PlayMethod, conditional episode buttons, `loadEpisode` race fix, app-wide Jellyfin remote control | ✅ done, **needs manual test** |
|
||||
|
||||
Verification gate for the whole branch: `bun run typecheck` ✅, `bun test utils/` ✅
|
||||
(32 tests). Note: project-wide `bun run check` shows ~124 pre-existing CRLF errors —
|
||||
a Windows `core.autocrlf` artifact, unrelated to this work (see §6).
|
||||
|
||||
---
|
||||
|
||||
## 2. Sub-project A — Device profiles & capability detection
|
||||
|
||||
**Spec:** `specs/2026-05-21-chromecast-profiles-design.md` · **Plan:**
|
||||
`plans/2026-05-21-chromecast-profiles.md` · **Commits:** `bcfa8c6d`..`73214f5d`
|
||||
|
||||
Replaced the two static device profiles with `detectCapabilities()` +
|
||||
`buildChromecastProfile()` (`utils/casting/capabilities.ts`, `buildProfile.ts`), a
|
||||
unified `loadCastMedia()` (`utils/casting/castLoad.ts`) with downgrade-on-failure,
|
||||
and `chromecastProfile` / `chromecastMaxBitrate` settings replacing
|
||||
`enableH265ForChromecast`. Fixed the audio-index, media-source, and PlaySessionId
|
||||
load-path bugs.
|
||||
|
||||
**Verified:** all 7 test-matrix files cast successfully on the Chromecast HD,
|
||||
including a 50 Mb/s HEVC-10bit movie that previously crashed with status 2100.
|
||||
|
||||
---
|
||||
|
||||
## 3. Prep — Segment-skip reconciliation (PR #1367)
|
||||
|
||||
The chromecast branch and PR #1367 (`autoskip`) both carried segment-skip; #1367 was
|
||||
behind. The chromecast branch's fixes were backported to the `autoskip` branch
|
||||
(`useSegmentSkipper` auto-skip-by-identity, `Controls` stale-closure fix, `segments`
|
||||
cleanup) and the two dead hooks `useIntroSkipper`/`useCreditSkipper` (177 lines) were
|
||||
removed. **Commit `0990e479` is pushed to `origin/autoskip` — PR #1367 is updated.**
|
||||
The `page.tsx` plugin-lock lines were not backported (depend on a newer
|
||||
`PlatformDropdown` not on #1367's base — will arrive when #1367 rebases on develop).
|
||||
|
||||
---
|
||||
|
||||
## 4. Sub-project B — Track switching & multi-version
|
||||
|
||||
**Spec:** `specs/2026-05-21-chromecast-track-switching-design.md` · **Plan:**
|
||||
`plans/2026-05-21-chromecast-track-switching.md` · **Commits:** `3d65c3bb`..`23b4f20d`
|
||||
|
||||
`CastSelection` (`utils/casting/selection.ts`) is the single source of truth, carried
|
||||
in cast `customData` and read back by `hooks/useCastSelection.ts` with an optimistic
|
||||
pending overlay. Audio / subtitle / quality / version switching all reflect reality.
|
||||
The shared `BITRATES` ladder (`components/BitrateSelector.tsx`) was expanded; the cast
|
||||
quality menu filters it by device capability and media bitrate.
|
||||
|
||||
**Verified:** audio (Bleach JP↔FR), SubRip subtitles, and bitrate switching all work
|
||||
on hardware — which also confirms the `customData` round-trip.
|
||||
|
||||
---
|
||||
|
||||
## 5. Sub-project C — `casting-player.tsx` split
|
||||
|
||||
**Spec:** `specs/2026-05-22-chromecast-player-split-design.md` · **Plan:**
|
||||
`plans/2026-05-22-chromecast-player-split.md` · **Commits:** `02df2477`..`1ea7f0f4`
|
||||
|
||||
Decomposed the 1428-line god-component into a 574-line orchestrator + 6
|
||||
presentational components (`components/casting/player/`) + 4 hooks
|
||||
(`useCastPlayerItem`, `useCastEpisodes`, `useCastDismissGesture`,
|
||||
`useCastPlayerProgress`). Purely structural — zero behaviour change.
|
||||
|
||||
**Needs manual re-test:** the cast player has no unit tests; re-test cast + episode
|
||||
playback, track switching, scrub/trickplay, dismiss — behaviour must match pre-split.
|
||||
|
||||
---
|
||||
|
||||
## 6. Sub-project D — Session reporting & remote control
|
||||
|
||||
**Spec:** `specs/2026-05-22-chromecast-session-remote-control-design.md` · **Plan:**
|
||||
`plans/2026-05-22-chromecast-session-remote-control.md` · **Commits:**
|
||||
`288b390e`..`8b94f491`
|
||||
|
||||
- Cast sessions now report the real `PlayMethod` (`Transcode`/`DirectPlay`).
|
||||
- Episode Previous/Next buttons render only when an adjacent episode exists.
|
||||
- `loadEpisode`/`currentItem` stale-flash race fixed via `loadingEpisodeId`.
|
||||
- **App-wide Jellyfin remote control:** a `PlaybackController` contract
|
||||
(`utils/playback/playbackController.ts`); a pure WS-message mapper
|
||||
(`utils/playback/remoteCommands.ts`, unit-tested); `hooks/useRemoteControl.ts`
|
||||
dispatches to whichever player (cast / native video / music) is registered.
|
||||
`WebSocketProvider` advertises the commands and routes the messages.
|
||||
|
||||
**Needs manual test:** from the Jellyfin dashboard's active-session panel — pause /
|
||||
stop / seek / next / volume / "send message" against the cast and the native player.
|
||||
|
||||
---
|
||||
|
||||
## 7. Pending work (queue)
|
||||
|
||||
- **UX player sub-project** (not started) — 5 items the user reported:
|
||||
1. Trickplay preview window is truncated at the right screen edge / does not track
|
||||
the cursor.
|
||||
2. The progress-bar touch zone overlaps the 4-button episode row.
|
||||
3. The purple time label should sit directly above the progress cursor.
|
||||
4. Trickplay on the mini-player progress bar.
|
||||
5. A stop button on the mini-player.
|
||||
These now live in clean post-C files (`CastPlayerProgressBar`, `CastingMiniPlayer`).
|
||||
- **Repo hygiene** — add `* text=auto eol=lf` to `.gitattributes` (fixes the ~124
|
||||
CRLF errors in `bun run check`); de-duplicate `BitRateSheet.tsx` vs
|
||||
`BitrateSelector.tsx`; remove the unused `liveProgress` export from
|
||||
`useCastPlayerProgress.ts`.
|
||||
- **Custom Cast receiver** (deferred from sub-project A) — PR #1521 builds a custom
|
||||
CAF receiver. Decided to defer; revisit as its own sub-project. It would own
|
||||
subtitle rendering/styling (ASS, custom style — issues #1452, #1543) and cleaner
|
||||
session integrity. Sender-side work so far does not block it.
|
||||
- **Open review notes** (low severity, not fixed):
|
||||
- D: the `PlaybackController` registry is last-write-wins; the spec's "cast
|
||||
precedence" is not actually enforced (acceptable — one player at a time).
|
||||
- D: remote next/previous works for cast but is a no-op for the native video
|
||||
player (its episode navigation lives in `Controls`, not the screen).
|
||||
- A: `getStreamUrl` still takes `MediaSources[0]` internally — fine because
|
||||
`getPlaybackInfo` is called with `mediaSourceId`.
|
||||
|
||||
---
|
||||
|
||||
## 8. How to resume
|
||||
|
||||
1. **Test the branch.** Build from `refactor-chromecast` (`bun run android`). Run the
|
||||
sub-project C manual re-test and the sub-project D dashboard remote-control test.
|
||||
Sub-project A's matrix is `docs/chromecast-test-matrix.md`.
|
||||
2. **Push.** When satisfied, push `refactor-chromecast` to update draft PR #1402
|
||||
(46 commits unpushed). Decide first what to do with the two uncommitted files
|
||||
(see §9).
|
||||
3. **Continue.** Pick the next sub-project from §7 — likely the UX player sub-project.
|
||||
Each sub-project follows the brainstorm → spec → plan → subagent-execution cycle;
|
||||
specs/plans go in `docs/superpowers/`.
|
||||
|
||||
---
|
||||
|
||||
## 9. Working-tree / repo notes
|
||||
|
||||
- **Uncommitted, intentionally left for the user to decide:**
|
||||
- `docs/chromecast-test-matrix.md` — modified to list the user's actual library
|
||||
titles (anime/movies). If PR #1402 should not expose the personal library, revert
|
||||
this file to its generic committed form before pushing.
|
||||
- `scripts/find-test-media.ts` — untracked helper that queries Jellyfin and buckets
|
||||
the library against the test matrix. Commit it if useful, or leave it local.
|
||||
- **Commits:** this project does **not** use a `Co-Authored-By` trailer.
|
||||
- **GPG:** committing requires a warm `gpg-agent` cache; `~/.gnupg/gpg-agent.conf`
|
||||
was set to an 8h TTL.
|
||||
|
||||
---
|
||||
|
||||
## 10. Key decisions
|
||||
|
||||
- **#1367 is the source of truth for segment-skip** — backport flows chromecast →
|
||||
#1367, never the reverse.
|
||||
- **Custom receiver deferred** — the crash fixes (A) were decoupled from it; it is a
|
||||
future sub-project, not a blocker.
|
||||
- **Long-term goal:** support AirPlay and other cast protocols — the casting layer is
|
||||
kept protocol-agnostic (`CastProtocol`). The user has an iOS phone and an
|
||||
AirPlay-capable Samsung screen for testing.
|
||||
Reference in New Issue
Block a user