Files
streamyfin/docs/superpowers/chromecast-refactor-handoff.md
Uruk 788a3b7cfd 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.
2026-05-22 02:32:36 +02:00

8.8 KiB

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.