Improve reliability of track search
Diff
assets/images/album_art_test.jpg | 3 ---
assets/images/unknown_artist.jpg | 3 +++
shalom/src/subscriptions.rs | 5 ++++-
shalom/src/theme.rs | 4 ++--
shalom/src/magic/header_search.rs | 16 +++++++++++++---
shalom/src/pages/room/listen.rs | 54 +++++++++++++++++++++++++++++++++++++++++-------------
6 files changed, 52 insertions(+), 33 deletions(-)
@@ -1,3 +1,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4cae52d3c493233ab1681825b15059315572c035937c42937c829e6b359188b8
size 15475
@@ -1,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c7b209e222e78bc720c814b4696ce301bfe3b0c362a50c2289e5bc055b5fc139
size 6132
@@ -45,6 +45,7 @@
) -> image::Handle {
static CACHE: Lazy<Mutex<LruCache<Url, image::Handle>>> =
Lazy::new(|| Mutex::new(LruCache::new(NonZeroUsize::new(50).unwrap())));
static CLIENT: Lazy<reqwest::Client> = Lazy::new(reqwest::Client::new);
let url = url.into_url().unwrap();
@@ -52,7 +53,9 @@
return handle.clone();
}
let bytes = reqwest::get(url.clone())
let bytes = CLIENT
.get(url.clone())
.send()
.await
.unwrap()
.bytes()
@@ -225,7 +225,7 @@
Bedroom,
DiningRoom,
Sunset,
AlbumArtTest,
UnknownArtist,
}
impl Image {
@@ -250,7 +250,7 @@
Image::Bedroom => image!("../../assets/images/bedroom.jpg"),
Image::DiningRoom => image!("../../assets/images/dining_room.jpg"),
Image::Sunset => image!("../../assets/images/sunset-blur.jpg"),
Image::AlbumArtTest => image!("../../assets/images/album_art_test.jpg"),
Image::UnknownArtist => image!("../../assets/images/unknown_artist.jpg"),
}
}
@@ -44,6 +44,7 @@
let current_search_box_size = if open { BoxSize::Fill } else { BoxSize::Min };
HeaderSearch {
original_header: header.clone(),
header,
current_search_box_size,
input: iced::widget::text_input("Search...", search_query)
@@ -65,6 +66,7 @@
}
pub struct HeaderSearch<'a, M> {
original_header: Text<'a, Renderer>,
header: Text<'a, Renderer>,
current_search_box_size: BoxSize,
on_state_change: fn(bool) -> M,
@@ -87,7 +89,7 @@
fn layout(&self, renderer: &Renderer, limits: &Limits) -> Node {
let text_node = <iced::advanced::widget::Text<'_, Renderer> as Widget<M, Renderer>>::layout(
&self.header,
&self.original_header,
renderer,
limits,
);
@@ -255,10 +257,14 @@
*last_draw = Instant::now();
text_opacity.advance_by(elapsed);
self.header = self.header.clone().style(iced::theme::Text::Color(Color {
a: text_opacity.now(),
..Color::WHITE
}));
self.header = self
.header
.clone()
.style(iced::theme::Text::Color(Color {
a: text_opacity.now(),
..Color::WHITE
}))
.size(60.0 - (2.0 * (1.0 - text_opacity.now())));
search_box_size.advance_by(elapsed);
self.current_search_box_size = BoxSize::Fixed(Size {
@@ -22,7 +22,7 @@
subscriptions::{
download_image, find_fanart_urls, find_musicbrainz_artist, load_image, MaybePendingImage,
},
theme::{darken_image, trim_transparent_padding},
theme::{darken_image, trim_transparent_padding, Image},
widgets,
};
@@ -373,16 +373,16 @@
pub fn open(&self, search: String) -> SearchState {
match self {
Self::Open { results, .. } => Self::Open {
needs_result: !search.is_empty(),
waiting_for_result: !search.is_empty(),
search,
results: results.clone(),
needs_result: true,
waiting_for_result: true,
},
Self::Closed => Self::Open {
needs_result: !search.is_empty(),
waiting_for_result: !search.is_empty(),
search,
results: vec![],
needs_result: true,
waiting_for_result: true,
},
}
}
@@ -456,68 +456,78 @@
let results = FuturesUnordered::new();
for track in &res.tracks.items {
let image_url = track.album.images[0].url.to_string();
let image_url = track.album.images.last().map(|v| v.url.to_string());
let track_name = track.name.to_string();
let artist_name = track.artists.iter().map(|v| &v.name).join(", ");
let uri = track.uri.to_string();
results.push(
results.push(tokio::spawn(
async move {
let image = load_image(image_url, identity).await;
let image = load_album_art(image_url).await;
SearchResult::track(image, track_name, artist_name, uri)
}
.boxed(),
);
));
}
for artist in &res.artists.items {
let image_url = artist.images[0].url.to_string();
let image_url = artist.images.last().map(|v| v.url.to_string());
let artist_name = artist.name.to_string();
let uri = artist.uri.to_string();
results.push(
results.push(tokio::spawn(
async move {
let image = load_image(image_url, identity).await;
let image = load_album_art(image_url).await;
SearchResult::artist(image, artist_name, uri)
}
.boxed(),
);
));
}
for albums in &res.albums.items {
let image_url = albums.images[0].url.to_string();
let image_url = albums.images.last().map(|v| v.url.to_string());
let album_name = albums.name.to_string();
let uri = albums.uri.to_string();
results.push(
results.push(tokio::spawn(
async move {
let image = load_image(image_url, identity).await;
let image = load_album_art(image_url).await;
SearchResult::album(image, album_name, uri)
}
.boxed(),
);
));
}
for playlist in &res.playlists.items {
let image_url = playlist.images[0].url.to_string();
let image_url = playlist.images.last().map(|v| v.url.to_string());
let playlist_name = playlist.name.to_string();
let uri = playlist.uri.to_string();
results.push(
results.push(tokio::spawn(
async move {
let image = load_image(image_url, identity).await;
let image = load_album_art(image_url).await;
SearchResult::playlist(image, playlist_name, uri)
}
.boxed(),
);
));
}
results.map(Message::SpotifySearchResult)
results
.map(Result::unwrap)
.map(Message::SpotifySearchResult)
})
.chain(stream::once(future::ready(
Message::SpotifySearchResultDone,
))),
)
}
async fn load_album_art(image_url: Option<String>) -> Handle {
if let Some(image_url) = image_url {
load_image(image_url, identity).await
} else {
Image::UnknownArtist.into()
}
}
#[derive(Deserialize, Yokeable)]