From 708e9e3314cd31ab800f688c997ff04a9099a897 Mon Sep 17 00:00:00 2001 From: Jackson Goode Date: Tue, 24 Dec 2024 11:28:55 -0800 Subject: [PATCH 1/4] Sanitize all playlist descriptions --- psst-gui/src/webapi/client.rs | 59 +++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/psst-gui/src/webapi/client.rs b/psst-gui/src/webapi/client.rs index aecee6c3..8f8f300f 100644 --- a/psst-gui/src/webapi/client.rs +++ b/psst-gui/src/webapi/client.rs @@ -128,8 +128,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,15 +485,13 @@ impl WebApi { }, )), description: { - let desc = sanitize_str( - &DEFAULT, + let desc = Self::sanitize_and_clean_description( item.content .data .description .as_deref() .unwrap_or_default(), - ) - .unwrap_or_default(); + ); // This is roughly 3 lines of description, truncated if too long if desc.chars().count() > 55 { desc.chars().take(52).collect::() + "..." @@ -513,13 +510,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, }); @@ -623,6 +621,12 @@ impl WebApi { shows: show, }) } + + fn sanitize_and_clean_description(description: &str) -> String { + let sanitized = sanitize_str(&DEFAULT, description).unwrap_or_default(); + // Replace HTML entities with their actual characters + sanitized.replace("&", "&") + } } static GLOBAL_WEBAPI: OnceCell> = OnceCell::new(); @@ -1200,7 +1204,17 @@ 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)?; + + let result = result + .into_iter() + .map(|mut playlist| { + playlist.description = + Self::sanitize_and_clean_description(&playlist.description).into(); + playlist + }) + .collect(); + Ok(result) } @@ -1219,7 +1233,8 @@ 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 mut result: Playlist = self.load(request)?; + result.description = Self::sanitize_and_clean_description(&result.description).into(); Ok(result) } @@ -1318,14 +1333,24 @@ 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 + .into_iter() + .map(|mut playlist| { + playlist.description = + Self::sanitize_and_clean_description(&playlist.description).into(); + playlist + }) + .collect() + }); let shows = result.shows.map_or_else(Vector::new, |page| page.items); + Ok(SearchResults { query: query.into(), artists, albums, tracks, - playlists: playlist, + playlists, shows, }) } From 695d60c141e96873775250c280c85dd2e835b9b6 Mon Sep 17 00:00:00 2001 From: Jackson Goode Date: Fri, 27 Dec 2024 19:29:17 -0800 Subject: [PATCH 2/4] Better name --- psst-gui/src/webapi/client.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/psst-gui/src/webapi/client.rs b/psst-gui/src/webapi/client.rs index 8f8f300f..d8988af8 100644 --- a/psst-gui/src/webapi/client.rs +++ b/psst-gui/src/webapi/client.rs @@ -485,7 +485,7 @@ impl WebApi { }, )), description: { - let desc = Self::sanitize_and_clean_description( + let desc = Self::sanitize_html( item.content .data .description @@ -622,9 +622,8 @@ impl WebApi { }) } - fn sanitize_and_clean_description(description: &str) -> String { + fn sanitize_html(description: &str) -> String { let sanitized = sanitize_str(&DEFAULT, description).unwrap_or_default(); - // Replace HTML entities with their actual characters sanitized.replace("&", "&") } } @@ -1209,8 +1208,7 @@ impl WebApi { let result = result .into_iter() .map(|mut playlist| { - playlist.description = - Self::sanitize_and_clean_description(&playlist.description).into(); + playlist.description = Self::sanitize_html(&playlist.description).into(); playlist }) .collect(); @@ -1234,7 +1232,7 @@ impl WebApi { pub fn get_playlist(&self, id: &str) -> Result { let request = self.get(format!("v1/playlists/{}", id), None)?; let mut result: Playlist = self.load(request)?; - result.description = Self::sanitize_and_clean_description(&result.description).into(); + result.description = Self::sanitize_html(&result.description).into(); Ok(result) } @@ -1337,8 +1335,7 @@ impl WebApi { page.items .into_iter() .map(|mut playlist| { - playlist.description = - Self::sanitize_and_clean_description(&playlist.description).into(); + playlist.description = Self::sanitize_html(&playlist.description).into(); playlist }) .collect() From 1f9bdda09218362f3a63041f488d309ee568c470 Mon Sep 17 00:00:00 2001 From: Jackson Goode Date: Fri, 27 Dec 2024 19:35:29 -0800 Subject: [PATCH 3/4] Deserialize instead --- psst-gui/src/data/playlist.rs | 12 ++++++++++++ psst-gui/src/webapi/client.rs | 12 +----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/psst-gui/src/data/playlist.rs b/psst-gui/src/data/playlist.rs index 8af76b24..faf82cd2 100644 --- a/psst-gui/src/data/playlist.rs +++ b/psst-gui/src/data/playlist.rs @@ -2,6 +2,8 @@ use std::sync::Arc; use druid::{im::Vector, Data, Lens}; use serde::{Deserialize, Deserializer, Serialize}; +use sanitize_html::rules::predefined::DEFAULT; +use sanitize_html::sanitize_str; 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_sanitized_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_sanitized_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 d8988af8..f207b5dd 100644 --- a/psst-gui/src/webapi/client.rs +++ b/psst-gui/src/webapi/client.rs @@ -1204,15 +1204,6 @@ impl WebApi { pub fn get_playlists(&self) -> Result, Error> { let request = self.get("v1/me/playlists", None)?; let result: Vector = self.load_all_pages(request)?; - - let result = result - .into_iter() - .map(|mut playlist| { - playlist.description = Self::sanitize_html(&playlist.description).into(); - playlist - }) - .collect(); - Ok(result) } @@ -1231,8 +1222,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 mut result: Playlist = self.load(request)?; - result.description = Self::sanitize_html(&result.description).into(); + let result: Playlist = self.load(request)?; Ok(result) } From ceb15bc9797d415777626128d5fca28b8b065ab1 Mon Sep 17 00:00:00 2001 From: Jackson Goode Date: Fri, 27 Dec 2024 21:00:15 -0800 Subject: [PATCH 4/4] Cleanup --- psst-gui/src/data/playlist.rs | 6 +++--- psst-gui/src/webapi/client.rs | 38 ++++++++++++----------------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/psst-gui/src/data/playlist.rs b/psst-gui/src/data/playlist.rs index faf82cd2..3e004634 100644 --- a/psst-gui/src/data/playlist.rs +++ b/psst-gui/src/data/playlist.rs @@ -1,9 +1,9 @@ use std::sync::Arc; use druid::{im::Vector, Data, Lens}; -use serde::{Deserialize, Deserializer, Serialize}; 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}; @@ -31,7 +31,7 @@ pub struct Playlist { pub name: Arc, #[serde(skip_serializing_if = "Option::is_none")] pub images: Option>, - #[serde(deserialize_with = "deserialize_sanitized_description")] + #[serde(deserialize_with = "deserialize_description")] pub description: Arc, #[serde(rename = "tracks")] #[serde(deserialize_with = "deserialize_track_count")] @@ -93,7 +93,7 @@ where Ok(PlaylistTracksRef::deserialize(deserializer)?.total) } -fn deserialize_sanitized_description<'de, D>(deserializer: D) -> Result, D::Error> +fn deserialize_description<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { diff --git a/psst-gui/src/webapi/client.rs b/psst-gui/src/webapi/client.rs index f207b5dd..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, @@ -485,21 +486,21 @@ impl WebApi { }, )), description: { - let desc = Self::sanitize_html( - item.content - .data - .description - .as_deref() - .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 @@ -621,11 +622,6 @@ impl WebApi { shows: show, }) } - - fn sanitize_html(description: &str) -> String { - let sanitized = sanitize_str(&DEFAULT, description).unwrap_or_default(); - sanitized.replace("&", "&") - } } static GLOBAL_WEBAPI: OnceCell> = OnceCell::new(); @@ -1321,15 +1317,7 @@ 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 playlists = result.playlists.map_or_else(Vector::new, |page| { - page.items - .into_iter() - .map(|mut playlist| { - playlist.description = Self::sanitize_html(&playlist.description).into(); - playlist - }) - .collect() - }); + 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 {