diff options
Diffstat (limited to 'examples/gallery/src/civitai.rs')
-rw-r--r-- | examples/gallery/src/civitai.rs | 119 |
1 files changed, 82 insertions, 37 deletions
diff --git a/examples/gallery/src/civitai.rs b/examples/gallery/src/civitai.rs index 986b6bf2..04589030 100644 --- a/examples/gallery/src/civitai.rs +++ b/examples/gallery/src/civitai.rs @@ -1,5 +1,6 @@ use bytes::Bytes; use serde::Deserialize; +use sipper::{sipper, Straw}; use tokio::task; use std::fmt; @@ -10,6 +11,7 @@ use std::sync::Arc; pub struct Image { pub id: Id, url: String, + hash: String, } impl Image { @@ -40,45 +42,76 @@ impl Image { Ok(response.items) } - pub async fn download(self, size: Size) -> Result<Rgba, Error> { - let client = reqwest::Client::new(); - - let bytes = client - .get(match size { - Size::Original => self.url, - Size::Thumbnail => self - .url - .split("/") - .map(|part| { - if part.starts_with("width=") { - "width=640" - } else { - part - } - }) - .collect::<Vec<_>>() - .join("/"), + pub async fn blurhash( + self, + width: u32, + height: u32, + ) -> Result<Blurhash, Error> { + task::spawn_blocking(move || { + let pixels = blurhash::decode(&self.hash, width, height, 1.0)?; + + Ok::<_, Error>(Blurhash { + rgba: Rgba { + width, + height, + pixels: Bytes::from(pixels), + }, }) - .send() - .await? - .error_for_status()? - .bytes() - .await?; - - let image = task::spawn_blocking(move || { - Ok::<_, Error>( - image::ImageReader::new(io::Cursor::new(bytes)) - .with_guessed_format()? - .decode()? - .to_rgba8(), - ) }) - .await??; + .await? + } - Ok(Rgba { - width: image.width(), - height: image.height(), - pixels: Bytes::from(image.into_raw()), + pub fn download(self, size: Size) -> impl Straw<Rgba, Blurhash, Error> { + sipper(move |mut sender| async move { + let client = reqwest::Client::new(); + + if let Size::Thumbnail { width, height } = size { + let image = self.clone(); + + drop(task::spawn(async move { + if let Ok(blurhash) = image.blurhash(width, height).await { + sender.send(blurhash).await; + } + })); + } + + let bytes = client + .get(match size { + Size::Original => self.url, + Size::Thumbnail { width, .. } => self + .url + .split("/") + .map(|part| { + if part.starts_with("width=") { + format!("width={}", width * 2) // High DPI + } else { + part.to_owned() + } + }) + .collect::<Vec<_>>() + .join("/"), + }) + .send() + .await? + .error_for_status()? + .bytes() + .await?; + + let image = task::spawn_blocking(move || { + Ok::<_, Error>( + image::ImageReader::new(io::Cursor::new(bytes)) + .with_guessed_format()? + .decode()? + .to_rgba8(), + ) + }) + .await??; + + Ok(Rgba { + width: image.width(), + height: image.height(), + pixels: Bytes::from(image.into_raw()), + }) }) } } @@ -88,6 +121,11 @@ impl Image { )] pub struct Id(u32); +#[derive(Debug, Clone)] +pub struct Blurhash { + pub rgba: Rgba, +} + #[derive(Clone)] pub struct Rgba { pub width: u32, @@ -107,7 +145,7 @@ impl fmt::Debug for Rgba { #[derive(Debug, Clone, Copy)] pub enum Size { Original, - Thumbnail, + Thumbnail { width: u32, height: u32 }, } #[derive(Debug, Clone)] @@ -117,6 +155,7 @@ pub enum Error { IOFailed(Arc<io::Error>), JoinFailed(Arc<task::JoinError>), ImageDecodingFailed(Arc<image::ImageError>), + BlurhashDecodingFailed(Arc<blurhash::Error>), } impl From<reqwest::Error> for Error { @@ -142,3 +181,9 @@ impl From<image::ImageError> for Error { Self::ImageDecodingFailed(Arc::new(error)) } } + +impl From<blurhash::Error> for Error { + fn from(error: blurhash::Error) -> Self { + Self::BlurhashDecodingFailed(Arc::new(error)) + } +} |