# icy-metadata
[](https://crates.io/crates/icy-metadata)
[](https://docs.rs/icy-metadata)
[](https://deps.rs/repo/github/aschey/icy-metadata)

[](https://github.com/aschey/icy-metadata/actions/workflows/ci.yml)
[](https://codecov.io/gh/aschey/icy-metadata)


[icy-metadata](https://github.com/aschey/icy-metadata) is a library for reading
metadata returned from Icecast-compatible web servers.
## Installation
```sh
cargo add icy-metadata
```
## Features
- `reqwest` - adds convenience methods to set icy metadata headers on
`reqwest`'s client builder and request builder.
- `serde` - enables serialization/deserialization for metadata structs.
## Headers
Parse common Icecast headers from an HTTP response. `icy-metadata` will look for
several common aliases to find the header values. More information about Icecast
headers can be found at
[stream-meta.info](https://www.stream-meta.info/intro.html).
```rust,no_run
use icy_metadata::IcyHeaders;
use std::error::Error;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let stream = reqwest::get("https://some-cool-url.com/some-file.mp3").await?;
let icy_headers = IcyHeaders::parse_from_headers(stream.headers());
println!("{icy_headers:?}");
Ok(())
}
```
## Reading information contained within the stream
Some streams have information about the current track contained within the
stream itself. Wrapping the stream in an `IcyMetadataReader` provides an
interface to read those values.
```rust,no_run
use std::error::Error;
use std::num::NonZeroUsize;
use icy_metadata::{IcyHeaders, IcyMetadataReader, RequestIcyMetadata};
use stream_download::http::HttpStream;
use stream_download::http::reqwest::{self, Client};
use stream_download::storage::bounded::BoundedStorageProvider;
use stream_download::storage::memory::MemoryStorageProvider;
use stream_download::{Settings, StreamDownload};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// We need to add a header to tell the Icecast server that we can parse the metadata
// embedded within the stream itself.
let client = Client::builder().request_icy_metadata().build()?;
let stream =
HttpStream::new(client, "https://some-cool-url.com/some-file.mp3".parse()?).await?;
let icy_headers = IcyHeaders::parse_from_headers(stream.headers());
// buffer 5 seconds of audio
// bitrate (in kilobits) / bits per byte * bytes per kilobyte * 5 seconds
let prefetch_bytes = icy_headers.bitrate().unwrap() / 8 * 1024 * 5;
let reader = StreamDownload::from_stream(
stream,
// use bounded storage to keep the underlying size from growing indefinitely
BoundedStorageProvider::new(
MemoryStorageProvider,
// be liberal with the buffer size, you need to make sure it holds
// enough space to prevent any out-of-bounds reads
NonZeroUsize::new(512 * 1024).unwrap(),
),
Settings::default().prefetch_bytes(prefetch_bytes as u64),
)
.await?;
let metadata_reader = IcyMetadataReader::new(
reader,
// Since we requested icy metadata, the metadata interval header should be
// present in the response. This will allow us to parse the metadata
// within the stream.
icy_headers.metadata_interval(),
// Print the stream metadata whenever we receive new values
|metadata| println!("{metadata:?}\n"),
);
Ok(())
}
```
### Seeking within the stream
Seeking is supported with a few limitations. See the docs for
[`IcyMetadataReader`](https://docs.rs/icy_metadata/latest/icy_metadata/struct.IcyMetadataReader.html)
for details.
## Supported Rust Versions
The MSRV is currently `1.85.0`. Since Cargo's V3 resolver supports MSRV-aware
dependencies, we do not treat an MSRV bump as a breaking change.