update image loading

This commit is contained in:
Daniel Bulant 2024-11-17 22:44:56 +01:00
parent d08dd6dcfc
commit f3760ec4e8
No known key found for this signature in database
2 changed files with 65 additions and 33 deletions

View file

@ -1,11 +1,15 @@
use std::sync::Arc; use std::sync::{Arc, LazyLock};
use cushy::{kludgine::{AnyTexture, LazyTexture}, value::{CallbackDisconnected, CallbackHandle, Destination, Dynamic, Source, Value}, widgets::Image}; use cushy::{
kludgine::{AnyTexture, LazyTexture},
value::{CallbackDisconnected, CallbackHandle, Destination, Dynamic, Source, Value},
widgets::Image,
};
use futures_util::lock::Mutex; use futures_util::lock::Mutex;
use http_cache_reqwest::{CACacheManager, Cache, CacheMode, HttpCache, HttpCacheOptions}; use http_cache_reqwest::{CACacheManager, Cache, CacheMode, HttpCache, HttpCacheOptions};
use image::imageops::FilterType; use image::imageops::FilterType;
use reqwest::Client; use reqwest::Client;
use reqwest_middleware::ClientBuilder; use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use crate::rt::tokio_runtime; use crate::rt::tokio_runtime;
@ -16,13 +20,24 @@ pub trait ImageExt {
fn load_url(&mut self, url: Dynamic<Option<String>>) -> CallbackHandle; fn load_url(&mut self, url: Dynamic<Option<String>>) -> CallbackHandle;
fn with_url(mut self, url: Dynamic<Option<String>>) -> Self fn with_url(mut self, url: Dynamic<Option<String>>) -> Self
where Self: Sized where
Self: Sized,
{ {
self.load_url(url).persist(); self.load_url(url).persist();
self self
} }
} }
static CLIENT: LazyLock<ClientWithMiddleware> = LazyLock::new(|| {
ClientBuilder::new(Client::new())
.with(Cache(HttpCache {
mode: CacheMode::Default,
manager: CACacheManager::default(),
options: HttpCacheOptions::default(),
}))
.build()
});
impl ImageExt for Image { impl ImageExt for Image {
fn new_empty() -> Self { fn new_empty() -> Self {
Image::new(Dynamic::new(get_empty_texture())) Image::new(Dynamic::new(get_empty_texture()))
@ -31,15 +46,6 @@ impl ImageExt for Image {
/// Makes the image connected to a URL /// Makes the image connected to a URL
/// Calling this multiple times on a single image may cause memory leaks /// Calling this multiple times on a single image may cause memory leaks
fn load_url(&mut self, url: Dynamic<Option<String>>) -> CallbackHandle { fn load_url(&mut self, url: Dynamic<Option<String>>) -> CallbackHandle {
let client = ClientBuilder::new(Client::new())
.with(Cache(HttpCache {
mode: CacheMode::Default,
manager: CACacheManager::default(),
options: HttpCacheOptions::default(),
}))
.build();
// let texture = Dynamic::new(get_empty_texture()); // let texture = Dynamic::new(get_empty_texture());
match &mut self.contents { match &mut self.contents {
Value::Constant(_) => self.contents = Value::Dynamic(Dynamic::new(get_empty_texture())), Value::Constant(_) => self.contents = Value::Dynamic(Dynamic::new(get_empty_texture())),
@ -47,9 +53,9 @@ impl ImageExt for Image {
} }
let texture = match &self.contents { let texture = match &self.contents {
Value::Dynamic(dynamic) => dynamic, Value::Dynamic(dynamic) => dynamic,
_ => unreachable!() _ => unreachable!(),
}; };
let prev_request_join = Arc::new(Mutex::new(None::<JoinHandle<()>>)); let prev_request_join = Arc::new(Mutex::new(None::<JoinHandle<()>>));
url.for_each_try({ url.for_each_try({
let texture = texture.clone(); let texture = texture.clone();
@ -58,16 +64,18 @@ impl ImageExt for Image {
if texture_count <= 1 { if texture_count <= 1 {
return Err(CallbackDisconnected); return Err(CallbackDisconnected);
} }
println!("loading url {:?}", url);
let guard = tokio_runtime().enter(); let guard = tokio_runtime().enter();
let url = url.clone(); let url = url.clone();
let prev_request_join = prev_request_join.clone(); let prev_request_join = prev_request_join.clone();
let texture = texture.clone(); let texture = texture.clone();
let client = client.clone(); let client = CLIENT.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut prev_request_join = prev_request_join.lock().await; let mut prev_request_join = prev_request_join.lock().await;
if let Some(prev_request_join) = prev_request_join.take() { if let Some(prev_request_join) = prev_request_join.take() {
prev_request_join.abort(); prev_request_join.abort();
} }
println!("loading url {:?}", url);
if let Some(url) = url { if let Some(url) = url {
let texture = texture.clone(); let texture = texture.clone();
let client = client.clone(); let client = client.clone();
@ -75,8 +83,11 @@ impl ImageExt for Image {
let response = client.get(url).send().await.unwrap(); let response = client.get(url).send().await.unwrap();
let bytes = response.bytes().await.unwrap(); let bytes = response.bytes().await.unwrap();
let image = image::load_from_memory(&bytes).unwrap(); let image = image::load_from_memory(&bytes).unwrap();
let image = image.resize(128, 128, FilterType::Lanczos3); // let image = image.resize(128, 128, FilterType::Lanczos3);
let image_texture = LazyTexture::from_image(image, cushy::kludgine::wgpu::FilterMode::Linear); let image_texture = LazyTexture::from_image(
image,
cushy::kludgine::wgpu::FilterMode::Linear,
);
let image_texture = AnyTexture::Lazy(image_texture); let image_texture = AnyTexture::Lazy(image_texture);
texture.set(image_texture); texture.set(image_texture);
})); }));
@ -91,14 +102,9 @@ impl ImageExt for Image {
} }
} }
fn get_empty_texture() -> AnyTexture { fn get_empty_texture() -> AnyTexture {
AnyTexture::Lazy( AnyTexture::Lazy(LazyTexture::from_image(
LazyTexture::from_image( image::DynamicImage::ImageRgba8(image::ImageBuffer::new(1, 1)),
image::DynamicImage::ImageRgba8( cushy::kludgine::wgpu::FilterMode::Linear,
image::ImageBuffer::new(1, 1) ))
), }
cushy::kludgine::wgpu::FilterMode::Linear
)
)
}

View file

@ -8,7 +8,10 @@ use cushy::{
styles::{Dimension, DimensionRange}, styles::{Dimension, DimensionRange},
value::{Destination, Dynamic, Source}, value::{Destination, Dynamic, Source},
widget::{MakeWidget, WidgetInstance}, widget::{MakeWidget, WidgetInstance},
widgets::{label::LabelOverflow, Image, Label, Space, VirtualList}, widgets::{
label::{Displayable, LabelOverflow},
Image, Label, Space, VirtualList,
},
}; };
use itertools::Itertools; use itertools::Itertools;
use rspotify::model::SavedTrack; use rspotify::model::SavedTrack;
@ -35,7 +38,6 @@ fn get_or_create_track_image(
idx: usize, idx: usize,
create: impl FnOnce() -> WidgetInstance, create: impl FnOnce() -> WidgetInstance,
) -> WidgetInstance { ) -> WidgetInstance {
println!("Getting image");
let mut locked = track_images.lock().unwrap(); let mut locked = track_images.lock().unwrap();
if let Some(image) = locked.get(&idx) { if let Some(image) = locked.get(&idx) {
image.clone() image.clone()
@ -147,19 +149,43 @@ impl LikedSongsPage {
.and(track.map_each(|track| { .and(track.map_each(|track| {
track track
.as_ref() .as_ref()
.map(|track| track.track.album.name.clone().make_widget()) .map(|track| {
track
.track
.album
.name
.clone()
.into_label()
.overflow(LabelOverflow::Clip)
.make_widget()
})
.unwrap_or(Space::primary().make_widget()) .unwrap_or(Space::primary().make_widget())
})) }))
.and(track.map_each(|track| { .and(track.map_each(|track| {
track track
.as_ref() .as_ref()
.map(|track| track.added_at.to_string().make_widget()) .map(|track| {
track
.added_at
.to_string()
.into_label()
.overflow(LabelOverflow::Clip)
.make_widget()
})
.unwrap_or(Space::primary().make_widget()) .unwrap_or(Space::primary().make_widget())
})) }))
.and(track.map_each(|track| { .and(track.map_each(|track| {
track track
.as_ref() .as_ref()
.map(|track| track.track.duration.to_string().make_widget()) .map(|track| {
track
.track
.duration
.to_string()
.into_label()
.overflow(LabelOverflow::Clip)
.make_widget()
})
.unwrap_or(Space::primary().make_widget()) .unwrap_or(Space::primary().make_widget())
})) }))
.into_columns() .into_columns()