diff --git a/docs/superpowers/chromecast-refactor-handoff.md b/docs/superpowers/chromecast-refactor-handoff.md new file mode 100644 index 000000000..72882cb56 --- /dev/null +++ b/docs/superpowers/chromecast-refactor-handoff.md @@ -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.