Add casting: Chromecast + DLNA receivers#67
Open
savvystartagency wants to merge 4 commits into
Open
Conversation
Adds a Cast button to Movie, TV and Downloads pages plus a device-picker
modal and mini-controller. Streams anime mp4, anime Sl-Hls, vidsrc /
videasy / 2embed HLS captures, and completed local downloads to any
Chromecast-built-in device (incl. XGIMI Android TV projectors) or DLNA
renderer on the LAN.
Highlights
- New main-process module src/ipc/cast.js
- Discovery: bonjour-service (_googlecast._tcp) + dlnacasts3 SSDP
- LAN-routable HTTP server bound 0.0.0.0:<random>
- /file/:token range-supported local file serve
- /proxy/:token header-injecting proxy with on-the-fly HLS playlist
rewriting (segments + EXT-X-KEY / EXT-X-MAP child tokens) so
receivers always speak to a Referer-injecting proxy
- /sub/:token VTT with CORS, inline SRT->VTT conversion
- Single active session (mirrors PIP). castv2-client for Chromecast,
dlnacasts3 for DLNA. Clean disconnect on before-quit.
- Preload bridge: cast:* IPC surface modeled on existing PIP naming.
- Renderer
- useCast() hook (src/utils/useCast.js) for device list + session state
- CastPickerModal with recent / available device lists, CAST/DLNA badges
- CastMiniController with play/pause/scrubber/CC toggle/volume/stop
- CastIcon + CastingIcon in Icons.jsx
- Wired into MoviePage, TVPage, DownloadsPage; Sidebar status via
the global mini-controller variant
- Settings: new "Cast & Devices" section (auto-discover, include-DLNA,
auto-stop, preferred device, rescan, limitations notes).
- Storage: CAST_* keys for the above preferences.
Dependencies
- castv2-client (Chromecast control)
- bonjour-service (mDNS for _googlecast._tcp)
- dlnacasts3 (DLNA SSDP + AVTransport)
All pure JS, no native deps.
Owner
|
Looks interesting, I suppose you have a Device to test this? Because I do not xD... Also, do you understand the Code you shared here? |
Author
|
yes, I am using it on my projectors and my TV :) ;) |
Author
|
I am just adding some controls, will push shortly |
c831caf to
5df2c8a
Compare
…dded controls Session / stop reliability - tearDown quits the DefaultMediaReceiver via client.stop(player) before closing the socket (1.5s fallback), so Stop returns the device to its home screen instead of leaving the cast splash up. - On Chromecast load failure, tear down the session + emit session-ended so the receiver doesn't sit on the splash and the UI resets. - Force-HLS hint: hlsRemote parent token and rewritten variant-playlist child tokens carry isHls so a mislabeled / extensionless m3u8 is still rewritten through the proxy (segments keep the injected Referer). In-app playback while casting - When a device is connected, the in-app <webview> is suspended (src -> about:blank) so playback doesn't double up with the receiver. A centred overlay shows "Playing on <device>" with the full controller. Device controls - CastMiniController gains a stacked "modal" variant (device label, seek, back/forward 10s, play/pause, subtitle toggle, volume, stop) embedded directly in the casting overlay and in the picker popup when connected. Discovery noise / efficiency - Discovery is on-demand: pages no longer scan on mount. The picker and the Settings > Cast section start discovery on open and stop it on close. - The connected device is tracked independently of the live discovery list, so controls keep working while discovery is stopped. - DLNA SSDP is gated by the "Include DLNA" setting and throttled to once per 8s; the dlnacasts "[DLNACASTS] querying ssdp" debug line is silenced. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Discovery (root cause of "no devices found")
- Bind the mDNS socket to the LAN interface (new Bonjour({ interface }))
via getLocalIPv4(). On machines with an active VPN or a second adapter
the default bind landed on the wrong interface and received zero
responses, so no Chromecast / Chromecast-built-in (e.g. XGIMI) devices
ever appeared. Rebuild the instance if the routable IP changes (VPN
toggled / network switched) so discovery recovers without a restart.
- Keep the mDNS browser alive for the session and re-query on reopen
instead of tearing it down on every picker/settings close (the teardown
made already-found devices disappear).
- Restore page-mount discovery (autoDiscover) now that it's cheap: passive
mDNS listener, DLNA SSDP gated by setting + throttled to 8s, and the
dlnacasts "[DLNACASTS] querying ssdp" debug line silenced.
- Hold the "scanning" state for a ~5s response window so the picker shows
"Scanning the network…" instead of flashing "No devices found" (the
start-discovery IPC resolves immediately while responses trickle in).
Serving correctness (from code review)
- getLocalIPv4 is now subnet-aware: when a receiver is connected it picks
the local interface on the receiver's subnet, falling back to a
preferred adapter, then any non-internal IPv4, then loopback. Prevents
handing the receiver an unreachable URL on multi-NIC setups.
- Dedup proxy tokens by target+referer (makeProxyToken) so HLS playlist
re-fetches (seeks/variant switches) reuse tokens instead of growing
_serveTokens unbounded; gcServeTokens prunes the dedup index too.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The in-app player paused when the laptop display slept or the window was
occluded because the main window used backgroundThrottling: true and
nothing held a power-save blocker (pre-existing, unrelated to casting).
- Hold powerSaveBlocker("prevent-display-sleep") while in-app playback is
active; release on pause/stop, on window close, and on app quit.
- Release the blocker while casting — playback then lives on the receiver
and the laptop is free to sleep.
- Set backgroundThrottling: false on the main and pop-out windows so
occlusion / alt-tab no longer throttles or pauses media.
Wired via a new playback-keepawake IPC + setPlaybackKeepAwake preload
bridge, driven from MoviePage/TVPage on `playing && !cast.currentDevice`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a Cast button to Movie / TV / Downloads pages plus a device-picker modal and a mini-controller. Streams anime mp4, anime Sl-Hls, vidsrc / videasy / 2embed HLS captures, and completed local downloads to any Chromecast-built-in device (incl. XGIMI Android TV projectors) or DLNA renderer on the LAN.
Highlights
src/ipc/cast.js— main-process subsystem. Discovery viabonjour-service(_googlecast._tcp) +dlnacasts3SSDP. Single active session (mirrors PIP) viacastv2-clientfor Chromecast anddlnacasts3for DLNA. Clean disconnect onbefore-quit.0.0.0.0:<random>(separate from the 127.0.0.1 allmanga player server):/file/:token— range-supported local file serve/proxy/:token— header-injecting proxy with on-the-fly HLS playlist rewriting (segments +EXT-X-KEY/EXT-X-MAPchild tokens) so receivers always speak to a Referer-injecting proxy/sub/:token— VTT with CORS, inline SRT→VTT conversioncast:*IPC surface modelled on existing PIP naming.useCast()hook,CastPickerModal,CastMiniController(play/pause/scrubber/CC/volume/stop),CastIcon+CastingIcon. Wired into MoviePage, TVPage, DownloadsPage.CAST_*keys.Dependencies
castv2-client,bonjour-service,dlnacasts3— all pure JS, no native deps.Test plan
Known limitations (surfaced in Settings notes box)