1use std::error::Error;
2use std::num::NonZeroUsize;
3use std::time::Duration;
4
5use icy_metadata::{IcyHeaders, IcyMetadataReader, RequestIcyMetadata};
6use rodio::source::SeekError;
7use stream_download::http::HttpStream;
8use stream_download::http::reqwest::Client;
9use stream_download::source::DecodeError;
10use stream_download::storage::bounded::BoundedStorageProvider;
11use stream_download::storage::memory::MemoryStorageProvider;
12use stream_download::{Settings, StreamDownload};
13
14#[tokio::main]
15async fn main() -> Result<(), Box<dyn Error>> {
16 let (_stream, handle) = rodio::OutputStream::try_default()?;
17 let sink = rodio::Sink::try_new(&handle)?;
18
19 let client = Client::builder().request_icy_metadata().build()?;
22
23 let stream =
24 HttpStream::new(client, "https://ice6.somafm.com/insound-128-mp3".parse()?).await?;
25
26 let icy_headers = IcyHeaders::parse_from_headers(stream.headers());
27 println!("Icecast headers: {icy_headers:#?}\n");
28 println!("content type={:?}\n", stream.content_type());
29
30 let prefetch_bytes = icy_headers.bitrate().unwrap() / 8 * 1024 * 5;
33
34 let reader = match StreamDownload::from_stream(
35 stream,
36 BoundedStorageProvider::new(
38 MemoryStorageProvider,
39 NonZeroUsize::new(512 * 1024).unwrap(),
42 ),
43 Settings::default().prefetch_bytes(prefetch_bytes as u64),
44 )
45 .await
46 {
47 Ok(reader) => reader,
48 Err(e) => return Err(e.decode_error().await)?,
49 };
50 sink.append(rodio::Decoder::new(IcyMetadataReader::new(
51 reader,
52 icy_headers.metadata_interval(),
55 |metadata| println!("{metadata:#?}\n"),
57 ))?);
58
59 let handle = tokio::task::spawn_blocking(move || {
60 println!("seeking in 5 seconds\n");
61 std::thread::sleep(Duration::from_secs(5));
62 println!("seeking to beginning\n");
63 sink.try_seek(Duration::from_secs(0))?;
64 std::thread::sleep(Duration::from_secs(5));
65 println!("seeking to 10 seconds\n");
66 sink.try_seek(Duration::from_secs(10))?;
67 sink.sleep_until_end();
68 Ok::<_, SeekError>(())
69 });
70 handle.await??;
71 Ok(())
72}