From 22c2e4e2db9ad9d892ed5fb63d92254677f6dafd Mon Sep 17 00:00:00 2001 From: alyx Date: Mon, 1 Apr 2024 19:00:57 -0400 Subject: Reliable user info; Font refactor Hit a few more endpoints to fix missing images, fallback to default album art just in case. Refactor the font cache into its own file. --- src/config.rs | 53 ++++++++++++++++------------------------------------- 1 file changed, 16 insertions(+), 37 deletions(-) (limited to 'src/config.rs') diff --git a/src/config.rs b/src/config.rs index e635f4a..3c11bc8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,7 +6,8 @@ use std::pin::Pin; use std::time::*; use super::cache::AsyncCache; -use super::deserialize::{GetRecentTracks, GetUserInfo, Track, User}; +use super::deserialize::{GetRecentTracks, GetUserInfo, GetTrackInfo, Track, TrackStub, User}; +use super::font::{font_cache, FontCache}; use reqwest::{Client, StatusCode}; use dotenv::var; @@ -18,13 +19,9 @@ type CacheFuture = Pin = fn(&String) -> CacheFuture; type Cache = Arc>>>; -type FontFuture = CacheFuture>; -type FontGetter = CacheGetter>; -type FontCache = Cache>; - -type UserFuture = CacheFuture>; -type UserGetter = CacheGetter>; -type UserCache = Cache>; +type UserFuture = CacheFuture>; +type UserGetter = CacheGetter>; +type UserCache = Cache>; static INTERNAL_THEMES: &[(&str, &str)] = &[("plain", include_str!("themes/plain.hbs"))]; @@ -49,40 +46,22 @@ fn user_getter(username: &String) -> UserFuture { if tracksreq.status() == StatusCode::NOT_FOUND { return Err((StatusCode::NOT_FOUND, "User does not exist!")); } if tracksreq.status() == StatusCode::FORBIDDEN { return Err((StatusCode::FORBIDDEN, "You need to unprivate your song history!")); } - let tracksinfo = tracksreq.json::().await + let trackstub = tracksreq.json::().await .map_err(|e| {log::error!("Couldn't parse user.getRecentTracks for `{username}`: {e}"); (StatusCode::INTERNAL_SERVER_ERROR, "Couldn't parse user.getRecentTracks!")})? .recenttracks.track.into_iter().next().ok_or((StatusCode::UNPROCESSABLE_ENTITY, "You need to listen to some songs first!"))?; - Ok(Arc::new((userinfo, tracksinfo))) - }) -} - -fn font_getter(fontname: &String) -> FontFuture { - let fontname = urlencoding::encode(fontname.as_ref()).to_string(); - Box::pin(async move { - let Some(google_api_key) = STATE.google_api_key.clone() - else { - unreachable!(); - }; - - let fontreq = STATE.http.get(format!("https://www.googleapis.com/webfonts/v1/webfonts?key={}&family={fontname}", google_api_key)) + let trackreq = STATE.http.get(format!("https://ws.audioscrobbler.com/2.0/?method=track.getInfo&format=json&username={username}&api_key={}&track={}&artist={}", STATE.lastfm_api_key, trackstub.name, trackstub.artist.name)) .send().await - .map_err(|e| {log::error!("Failed to get info for font `{fontname}`: {e}"); (StatusCode::SERVICE_UNAVAILABLE, "Couldn't connect to Google Fonts!")})?; - if fontreq.status() == StatusCode::NOT_FOUND { return Err((StatusCode::NOT_FOUND, "Font does not exist!")); } - if fontreq.status() == StatusCode::FORBIDDEN { - log::error!("Invalid Google API key in config!"); - return Err((StatusCode::SERVICE_UNAVAILABLE, "This instance is not configured to support Google Fonts properly, please use a different font.")); - } + .map_err(|e| {log::error!("Failed to get tracks for user `{username}`: {e}"); (StatusCode::SERVICE_UNAVAILABLE, "Couldn't connect to last.fm!")})?; + if trackreq.status() == StatusCode::NOT_FOUND { return Err((StatusCode::NOT_FOUND, "Track does not exist!")); } - let cssreq = STATE.http.get(format!("https://fonts.googleapis.com/css2?family={fontname}")) - .send().await - .map_err(|e| {log::error!("Failed to get CSS for font `{fontname}`: {e}"); (StatusCode::SERVICE_UNAVAILABLE, "Couldn't download font CSS!")})?; + let trackinfo = trackreq.json::().await + .map_err(|e| {log::error!("Couldn't parse track.getInfo for `{}` by `{}` on behalf of {username}: {e}", trackstub.name, trackstub.artist.name); (StatusCode::INTERNAL_SERVER_ERROR, "Couldn't parse track.getInfo!")})?.track; - Ok(cssreq.text().await.unwrap().into()) + Ok(Arc::new((userinfo, trackinfo, trackstub))) }) } - #[derive(Debug)] enum Whitelist { Exclusive{cache: UserCache, whitelist: BTreeSet}, @@ -95,11 +74,11 @@ pub struct State { default_theme: Arc, send_refresh_header: bool, - http: Client, + pub(crate) http: Client, handlebars: Handlebars<'static>, - google_api_key: Option>, + pub(crate) google_api_key: Option>, google_fonts_cache: FontCache, whitelist: Whitelist, @@ -146,7 +125,7 @@ impl State { }, google_api_key: var("LFME_GOOGLE_API_KEY").map(Into::into).ok(), - google_fonts_cache: Arc::new(RwLock::new(AsyncCache::new(Duration::from_secs(86400), font_getter as FontGetter))), + google_fonts_cache: font_cache(), whitelist: { let load_whitelist = || -> Option> { @@ -178,7 +157,7 @@ impl State { self.google_fonts_cache.write().await.get_owned(font).await } pub fn has_google_api_key(&self) -> bool { self.google_api_key.is_some() } - pub async fn get_userinfo(&self, user: &String) -> (Result, (StatusCode, &'static str)>, Duration) { + pub async fn get_userinfo(&self, user: &String) -> (Result, (StatusCode, &'static str)>, Duration) { match &self.whitelist { Whitelist::Open{default_cache, whitelist_cache, whitelist} => { if whitelist.contains(user) { -- cgit v1.2.3-54-g00ecf