<p align="center">
<h1 align="center">๐ฐ๏ธ oxicast</h1>
<p align="center">
Async Google Cast (Chromecast) client for Rust โ discover, connect, and control Cast devices with tokio.
</p>
</p>
<p align="center">
<a href="https://crates.io/crates/oxicast"><img src="https://img.shields.io/crates/v/oxicast.svg" alt="crates.io"></a>
<a href="https://docs.rs/oxicast"><img src="https://img.shields.io/docsrs/oxicast" alt="docs.rs"></a>
<a href="https://github.com/denniskribl/oxicast/actions"><img src="https://github.com/denniskribl/oxicast/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="https://codecov.io/gh/denniskribl/oxicast"><img src="https://codecov.io/gh/denniskribl/oxicast/branch/main/graph/badge.svg" alt="Coverage"></a>
<a href="#license"><img src="https://img.shields.io/badge/license-MIT%2FApache--2.0-blue" alt="License"></a>
</p>
<p align="center">
<a href="#-quickstart">Quickstart</a> ยท
<a href="https://docs.rs/oxicast">API Docs</a> ยท
<a href="./examples">Examples</a> ยท
<a href="./docs">Guide</a>
</p>
---
## What is this?
A fully async Rust client for the Google Cast protocol (Chromecast, Google TV, Cast-enabled speakers). Connect by IP or discover devices via mDNS, launch apps, load media, control playback, and react to device events โ all through a clean `async`/`await` API built on [tokio](https://tokio.rs).
```rust
let client = CastClient::connect("192.168.1.100", 8009).await?;
client.launch_app(&CastApp::DefaultMediaReceiver).await?;
client.load_media(&MediaInfo::new(url, "video/mp4"), true, 0.0).await?;
client.pause().await?;
client.seek(60.0).await?;
client.play().await?;
```
---
<a id="-quickstart"></a>
## ๐ Quickstart
### 1. Install
```toml
[dependencies]
oxicast = "0.0.1"
tokio = { version = "1", features = ["full"] }
```
### 2. Connect and play
```rust
use oxicast::{CastClient, CastApp, CastEvent, MediaInfo};
use std::time::Duration;
#[tokio::main]
async fn main() -> oxicast::Result<()> {
// Discover devices on the network
let devices = oxicast::discovery::discover_devices(Duration::from_secs(3)).await?;
let device = match devices.first() {
Some(d) => d,
None => { eprintln!("No devices found"); return Ok(()); }
};
println!("Found: {} at {}", device.name, device.ip);
// Connect and play
let client = CastClient::connect(&device.ip.to_string(), device.port).await?;
client.launch_app(&CastApp::DefaultMediaReceiver).await?;
client.load_media(
&MediaInfo::new("https://example.com/video.mp4", "video/mp4"),
true, 0.0,
).await?;
// React to events
loop {
tokio::select! {
Some(event) = client.next_event() => match event {
CastEvent::MediaStatusChanged(s) => {
println!("{:?} at {:.1}s", s.player_state, s.current_time);
}
CastEvent::Disconnected(_) => break,
_ => {}
},
_ = tokio::signal::ctrl_c() => {
client.disconnect().await?;
break;
}
}
}
Ok(())
}
```
Or connect directly by IP โ discovery is optional:
```rust
let client = CastClient::connect("192.168.1.100", 8009).await?;
```
---
## โจ Features
| ๐ **Async-native** | Built on tokio โ full `async`/`await`, no blocking calls |
| ๐ **Auto heartbeat** | PING/PONG managed invisibly in the background |
| โก **Instant commands** | Separate reader/writer tasks โ commands never wait for the read loop |
| ๐ **Reactive status** | `watch` channels for always-fresh media and receiver state |
| ๐ **Auto-reconnect** | Exponential backoff with jitter, serialized with manual reconnect |
| ๐ **mDNS discovery** | Scan the network or stream devices as they appear |
| ๐ก๏ธ **Typed errors** | Structured enum for connection, protocol, and media failures |
| ๐งต **Thread-safe** | `Clone + Send + Sync` โ share across tasks freely |
| ๐ **Local file casting** | Built-in HTTP server serves files to Chromecast (opt-in `serve` feature) |
### Feature flags
| `discovery` | โ
| mDNS device scanning via `mdns-sd` |
| `serve` | โ | HTTP file server for casting local files |
```toml
# Disable discovery, enable local file serving
oxicast = { version = "0.0.1", default-features = false, features = ["serve"] }
```
---
## ๐ Documentation
| **[Getting Started](docs/getting-started.md)** | Connect, discover, play media, handle events |
| **[API Overview](docs/api-overview.md)** | Full API surface with code examples |
| **[Architecture](docs/architecture.md)** | Split-stream reactor, task lifecycle, reconnect, event delivery |
| **[Error Handling](docs/error-handling.md)** | Error enum, device errors, timeouts |
| **[CHANGELOG](CHANGELOG.md)** | Version history |
---
## ๐ Examples
| [`discover_and_play`](./examples/discover_and_play.rs) | mDNS scan, connect, load HLS stream, monitor events |
| [`media_control`](./examples/media_control.rs) | Pause, seek, volume, resume |
| [`event_monitor`](./examples/event_monitor.rs) | Print all events in real-time |
| [`custom_namespace`](./examples/custom_namespace.rs) | Send/receive on custom namespaces |
| [`device_test`](./examples/device_test.rs) | 14-step integration test against real hardware |
```sh
# Run the device test (auto-discovers or pass IP)
cargo run --example device_test --all-features
cargo run --example device_test --all-features -- 192.168.1.100
# With protocol-level tracing
RUST_LOG=oxicast=trace cargo run --example device_test --all-features
```
---
## License
Licensed under either of [Apache License 2.0](./LICENSE-APACHE) or [MIT](./LICENSE-MIT) at your option.
---
## Trademarks
Google Cast and Chromecast are trademarks of Google LLC. This project is not affiliated with or endorsed by Google.