icy-metadata is a library for reading metadata returned from Icecast-compatible web servers.
cargo add icy-metadata
reqwest
- adds convenience methods to set icy metadata headers onreqwest
's client builder and request builder.serde
- enables serialization/deserialization for metadata structs.
Parse common Icecast headers from an HTTP response. icy-metadata
will look for
several common aliases to find the header values.
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(())
}
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.
use std::error::Error;
use std::num::NonZeroUsize;
use icy_metadata::{IcyHeaders, IcyMetadataReader, RequestIcyMetadata};
use stream_download::http::reqwest::{self, Client};
use stream_download::http::HttpStream;
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 is supported with a few limitations. See the docs for
IcyMetadataReader
for details.
The MSRV is currently 1.70.0
.