use std::error::Error;
use std::num::NonZeroUsize;
use std::time::Duration;
use icy_metadata::{IcyHeaders, IcyMetadataReader, RequestIcyMetadata};
use rodio::source::SeekError;
use stream_download::http::HttpStream;
use stream_download::http::reqwest::Client;
use stream_download::source::DecodeError;
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>> {
let stream_handle = rodio::OutputStreamBuilder::open_default_stream()?;
let sink = rodio::Sink::connect_new(stream_handle.mixer());
let client = Client::builder().request_icy_metadata().build()?;
let stream =
HttpStream::new(client, "https://ice6.somafm.com/insound-128-mp3".parse()?).await?;
let icy_headers = IcyHeaders::parse_from_headers(stream.headers());
println!("Icecast headers: {icy_headers:#?}\n");
println!("content type={:?}\n", stream.content_type());
let prefetch_bytes = icy_headers.bitrate().unwrap() / 8 * 1024 * 5;
let reader = match StreamDownload::from_stream(
stream,
BoundedStorageProvider::new(
MemoryStorageProvider,
NonZeroUsize::new(512 * 1024).unwrap(),
),
Settings::default().prefetch_bytes(prefetch_bytes as u64),
)
.await
{
Ok(reader) => reader,
Err(e) => return Err(e.decode_error().await)?,
};
sink.append(
rodio::Decoder::builder()
.with_seekable(true)
.with_data(IcyMetadataReader::new(
reader,
icy_headers.metadata_interval(),
|metadata| println!("{metadata:#?}\n"),
))
.build()?,
);
let handle = tokio::task::spawn_blocking(move || {
println!("seeking in 5 seconds\n");
std::thread::sleep(Duration::from_secs(5));
println!("seeking to beginning\n");
sink.try_seek(Duration::from_secs(0))?;
std::thread::sleep(Duration::from_secs(5));
println!("seeking to 10 seconds\n");
sink.try_seek(Duration::from_secs(10))?;
sink.sleep_until_end();
Ok::<_, SeekError>(())
});
handle.await??;
Ok(())
}