Skip to content

feat(cast): Chromecast support for mobile player#258

Merged
ProdigyV21 merged 3 commits into
ProdigyV21:mainfrom
chillpill244:player_cast
May 29, 2026
Merged

feat(cast): Chromecast support for mobile player#258
ProdigyV21 merged 3 commits into
ProdigyV21:mainfrom
chillpill244:player_cast

Conversation

@chillpill244
Copy link
Copy Markdown
Contributor

@chillpill244 chillpill244 commented May 23, 2026

Summary

  • Integrates Google Cast SDK (play-services-cast-framework:21.4.0, mediarouter:1.7.0)
  • New CastManager singleton manages session lifecycle and exposes castState: StateFlow<CastState>; all cast control (play/pause/seek/skip) routes through RemoteMediaClient
  • New CastOptionsProvider registered in AndroidManifest.xml with notification actions (rewind, play/pause, forward, stop casting)
  • Mobile-only cast button in player controls; "Casting to [device]" banner shown while active
  • While casting: local ExoPlayer pauses; progress bar polls Chromecast at 500ms intervals; play/pause, seek bar drag, and double-tap skip all route to RemoteMediaClient
  • On disconnect: ExoPlayer resumes from the last position reported by Chromecast
  • Theme fix: Theme.ArflixTV.Mobile now extends Theme.AppCompat.NoActionBar so MediaRouteChooserDialog opens without crashing
  • ProGuard rules added for Cast SDK classes

Test plan

  • TV build: cast button absent, all existing controls work identically
  • Phone, no Chromecast nearby: cast button visible, chooser dialog opens gracefully
  • Phone + Chromecast on same WiFi: tap cast → select device → banner appears, video plays on TV from same position
  • Play/pause, seek bar, and double-tap skip (+/–10s) all control Chromecast while casting
  • Disconnect: local video resumes from cast position within ~1s
  • No Google Play Services (sideload): CastState.NotAvailable, button hidden, no crash

@ProdigyV21
Copy link
Copy Markdown
Owner

Great pull, wanted this for a while too. Do you have screenshots of tests where its working?

The main issue is that ARVIO often plays streams that require request headers like User-Agent, Referer, Origin, or proxy headers. Local ExoPlayer applies those headers, but the Chromecast path only sends the raw stream URL to Chromecast. That means many addon/scraper/debrid streams may fail on Chromecast with 403/no playback even though they work locally.

So I think this needs one more pass before merge:

  • either only show/allow casting for streams that do not require custom headers
  • or add a cast-compatible proxy/receiver path that preserves the required headers
  • or clearly document that Chromecast only works for simple public MP4/HLS/DASH URLs

Compile checks passed, so this is not a build problem. It is a runtime compatibility problem with real ARVIO stream sources.

@chillpill244
Copy link
Copy Markdown
Contributor Author

Tested with IPTV streams that require a custom User-Agent header — casting works because Chromecast's built-in Chrome browser agent is accepted by the server. Streams where headers are strictly enforced will fail silently on the device (no playback), same as an unsupported format. The majority of debrid and IPTV HLS streams cast correctly. A future improvement could detect Chromecast playback errors and surface a warning — but hiding the cast button preemptively would break working streams.

@ProdigyV21
Copy link
Copy Markdown
Owner

This is a good feature and I do want Chromecast support, but I don't think this is safe to merge yet.

Right now the PR conflicts with current main in PlayerScreen.kt, so it needs to be rebased first. When resolving it, please keep both the new cast logic and the existing subtitle/settings logic.

Bigger issue: CastManager currently sends only the stream URL to Chromecast. A lot of ARVIO streams need headers/cookies/referer/user-agent/proxy headers to play. Those headers are used by ExoPlayer locally, but Chromecast will not automatically get them, so many streams may fail when casting.

To make it safe, either:

  • only allow casting for streams with no required headers, and disable/hide cast for unsupported streams, or
  • add a local/proxy cast URL that injects the needed headers for Chromecast.

After that we should test direct streams, IPTV streams, and addon/debrid streams before merging.

chillpill244 and others added 3 commits May 28, 2026 18:38
- pointerInput key changed from Unit to isCasting so the gesture handler
  restarts when casting state changes, capturing the updated queueControlsSeek
  lambda that routes double-tap to castManager.skipForward/Back
- track lastCastPositionMs during the 500ms poll loop; use it to seek
  ExoPlayer on disconnect (remoteMediaClient is null by then so
  getApproximatePosition() always returned 0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Chromecast's default receiver fetches stream URLs directly without any
custom HTTP headers. Streams that set proxyHeaders.request (e.g. addons
requiring Authorization or Referer) would fail silently on the device.

Check behaviorHints.proxyHeaders.request and hide the cast button when
any per-stream headers are required, keeping it visible for IPTV and
debrid streams where auth is embedded in the URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@chillpill244
Copy link
Copy Markdown
Contributor Author

Done — rebased onto current main (conflict in PlayerScreen.kt resolved, keeping both the subtitle settings LaunchedEffects/Regexes and all cast logic) and addressed the header safety concern.

How the header issue is handled:

Added a streamNeedsHeaders check derived from behaviorHints.proxyHeaders.request. The cast button is now hidden whenever the currently selected stream carries explicit per-stream request headers (Authorization, Referer, etc.) that ExoPlayer applies locally but Chromecast's default receiver would never send.

val streamNeedsHeaders = uiState.selectedStream
    ?.behaviorHints?.proxyHeaders?.request?.isNotEmpty() == true
// ...
if (isTouchDevice && castAvailable && !streamNeedsHeaders) { /* cast button */ }

Why this is safe for IPTV and debrid streams:

IPTV streams (Xtream Codes, M3U) and debrid-resolved links (Real-Debrid, AllDebrid) embed their auth as tokens in the URL itself — they don't set proxyHeaders.request. Tested casting IPTV streams that require a custom User-Agent: those work because Chromecast's built-in Chrome UA is accepted by the server, and the token is in the URL. The cast button remains visible for those.

The button is only hidden for Stremio addon streams that explicitly set proxyHeaders.request (e.g. specific scrapers requiring a signed Referer or Authorization header) — those would 403 on the Chromecast receiver anyway.

@ProdigyV21 ProdigyV21 merged commit 8e4eadf into ProdigyV21:main May 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants