1use std::error::Error;
2use std::num::NonZeroUsize;
3
4use icy_metadata::{IcyHeaders, IcyMetadataReader, RequestIcyMetadata};
5use stream_download::http::HttpStream;
6use stream_download::http::reqwest::Client;
7use stream_download::source::DecodeError;
8use stream_download::storage::bounded::BoundedStorageProvider;
9use stream_download::storage::memory::MemoryStorageProvider;
10use stream_download::{Settings, StreamDownload};
11
12#[tokio::main]
13async fn main() -> Result<(), Box<dyn Error>> {
14 let stream_handle = rodio::OutputStreamBuilder::open_default_stream()?;
15 let sink = rodio::Sink::connect_new(stream_handle.mixer());
16
17 let client = Client::builder().request_icy_metadata().build()?;
20
21 let stream =
22 HttpStream::new(client, "https://ice6.somafm.com/insound-128-mp3".parse()?).await?;
23
24 let icy_headers = IcyHeaders::parse_from_headers(stream.headers());
25 println!("Icecast headers: {icy_headers:#?}\n");
26 println!("content type={:?}\n", stream.content_type());
27
28 let prefetch_bytes = icy_headers.bitrate().unwrap() / 8 * 1024 * 5;
31
32 let reader = match StreamDownload::from_stream(
33 stream,
34 BoundedStorageProvider::new(
36 MemoryStorageProvider,
37 NonZeroUsize::new(512 * 1024).unwrap(),
40 ),
41 Settings::default().prefetch_bytes(prefetch_bytes as u64),
42 )
43 .await
44 {
45 Ok(reader) => reader,
46 Err(e) => return Err(e.decode_error().await)?,
47 };
48 sink.append(rodio::Decoder::new(IcyMetadataReader::new(
49 reader,
50 icy_headers.metadata_interval(),
53 |metadata| println!("{metadata:#?}\n"),
55 ))?);
56
57 let handle = tokio::task::spawn_blocking(move || {
58 sink.sleep_until_end();
59 });
60 handle.await?;
61 Ok(())
62}