diff --git a/psst-gui/src/data/playlist.rs b/psst-gui/src/data/playlist.rs index 8af76b24..3e004634 100644 --- a/psst-gui/src/data/playlist.rs +++ b/psst-gui/src/data/playlist.rs @@ -1,6 +1,8 @@ use std::sync::Arc; use druid::{im::Vector, Data, Lens}; +use sanitize_html::rules::predefined::DEFAULT; +use sanitize_html::sanitize_str; use serde::{Deserialize, Deserializer, Serialize}; use crate::data::{user::PublicUser, Image, Promise, Track, TrackId}; @@ -29,6 +31,7 @@ pub struct Playlist { pub name: Arc, #[serde(skip_serializing_if = "Option::is_none")] pub images: Option>, + #[serde(deserialize_with = "deserialize_description")] pub description: Arc, #[serde(rename = "tracks")] #[serde(deserialize_with = "deserialize_track_count")] @@ -89,3 +92,12 @@ where Ok(PlaylistTracksRef::deserialize(deserializer)?.total) } + +fn deserialize_description<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let description: String = String::deserialize(deserializer)?; + let sanitized = sanitize_str(&DEFAULT, &description).unwrap_or_default(); + Ok(Arc::from(sanitized.replace("&", "&"))) +} diff --git a/psst-gui/src/webapi/client.rs b/psst-gui/src/webapi/client.rs index aecee6c3..4a6b3fbb 100644 --- a/psst-gui/src/webapi/client.rs +++ b/psst-gui/src/webapi/client.rs @@ -16,8 +16,6 @@ use druid::{ use itertools::Itertools; use once_cell::sync::OnceCell; use parking_lot::Mutex; -use sanitize_html::rules::predefined::DEFAULT; -use sanitize_html::sanitize_str; use serde::{de::DeserializeOwned, Deserialize}; use serde_json::json; use ureq::{Agent, Request, Response}; @@ -39,6 +37,9 @@ use crate::{ }; use super::{cache::WebApiCache, local::LocalTrackManager}; +use sanitize_html::rules::predefined::DEFAULT; +use sanitize_html::sanitize_str; + pub struct WebApi { session: SessionService, agent: Agent, @@ -128,8 +129,7 @@ impl WebApi { /// Send a request with a empty JSON object, throw away the response body. /// Use for POST/PUT/DELETE requests. fn send_empty_json(&self, request: Request) -> Result<(), Error> { - Self::with_retry(|| Ok(request.clone().send_string("{}")?)) - .map(|_| ()) + Self::with_retry(|| Ok(request.clone().send_string("{}")?)).map(|_| ()) } /// Send a request and return the deserialized JSON body. Use for GET @@ -486,23 +486,21 @@ impl WebApi { }, )), description: { - let desc = sanitize_str( - &DEFAULT, - item.content - .data - .description - .as_deref() - .unwrap_or_default(), - ) - .unwrap_or_default(); + let desc = item + .content + .data + .description + .as_deref() + .unwrap_or_default() + .to_string(); // This is roughly 3 lines of description, truncated if too long if desc.chars().count() > 55 { desc.chars().take(52).collect::() + "..." } else { desc } - .into() - }, + } + .into(), track_count: item.content.data.attributes.as_ref().and_then( |attrs| { attrs @@ -513,13 +511,14 @@ impl WebApi { ), owner: PublicUser { id: Arc::from(""), - display_name: Arc::from(item - .content - .data - .owner_v2 - .as_ref() - .map(|owner| owner.data.name.as_str()) - .unwrap_or_default()) + display_name: Arc::from( + item.content + .data + .owner_v2 + .as_ref() + .map(|owner| owner.data.name.as_str()) + .unwrap_or_default(), + ), }, collaborative: false, }); @@ -1200,7 +1199,7 @@ impl WebApi { // https://developer.spotify.com/documentation/web-api/reference/get-a-list-of-current-users-playlists pub fn get_playlists(&self) -> Result, Error> { let request = self.get("v1/me/playlists", None)?; - let result = self.load_all_pages(request)?; + let result: Vector = self.load_all_pages(request)?; Ok(result) } @@ -1219,7 +1218,7 @@ impl WebApi { // https://developer.spotify.com/documentation/web-api/reference/get-playlist pub fn get_playlist(&self, id: &str) -> Result { let request = self.get(format!("v1/playlists/{}", id), None)?; - let result = self.load(request)?; + let result: Playlist = self.load(request)?; Ok(result) } @@ -1318,14 +1317,15 @@ impl WebApi { let artists = result.artists.map_or_else(Vector::new, |page| page.items); let albums = result.albums.map_or_else(Vector::new, |page| page.items); let tracks = result.tracks.map_or_else(Vector::new, |page| page.items); - let playlist = result.playlists.map_or_else(Vector::new, |page| page.items); + let playlists = result.playlists.map_or_else(Vector::new, |page| page.items); let shows = result.shows.map_or_else(Vector::new, |page| page.items); + Ok(SearchResults { query: query.into(), artists, albums, tracks, - playlists: playlist, + playlists, shows, }) }