From 5278b78bcc132fbb62901e2c859021c69cb161c3 Mon Sep 17 00:00:00 2001 From: Thang Pham Date: Sat, 15 Jul 2023 16:56:39 -0400 Subject: [PATCH] update the cache usage to `ttl_cache` functions --- spotify_player/src/client/handlers.rs | 2 +- spotify_player/src/client/mod.rs | 56 +++++++++++++++++++-------- spotify_player/src/event/page.rs | 2 +- spotify_player/src/event/window.rs | 2 +- spotify_player/src/state/data.rs | 29 ++++++++------ spotify_player/src/state/mod.rs | 1 - spotify_player/src/ui/page.rs | 6 +-- spotify_player/src/ui/playback.rs | 2 +- 8 files changed, 64 insertions(+), 36 deletions(-) diff --git a/spotify_player/src/client/handlers.rs b/spotify_player/src/client/handlers.rs index 4ccd807a..e89007df 100644 --- a/spotify_player/src/client/handlers.rs +++ b/spotify_player/src/client/handlers.rs @@ -178,7 +178,7 @@ pub async fn start_player_event_watchers( // request new context's data if not found in memory if let Some(id) = id { - if !state.data.read().caches.context.contains(&id.uri()) { + if !state.data.read().caches.context.contains_key(&id.uri()) { client_pub .send(ClientRequest::GetContext(id.clone())) .unwrap_or_default(); diff --git a/spotify_player/src/client/mod.rs b/spotify_player/src/client/mod.rs index 6dde762c..5c393558 100644 --- a/spotify_player/src/client/mod.rs +++ b/spotify_player/src/client/mod.rs @@ -206,12 +206,17 @@ impl Client { let client = lyric_finder::Client::from_http_client(&self.http); let query = format!("{track} {artists}"); - if !state.data.read().caches.lyrics.contains(&query) { + if !state.data.read().caches.lyrics.contains_key(&query) { let result = client.get_lyric(&query).await.context(format!( "failed to get lyric for track {track} - artists {artists}" ))?; - state.data.write().caches.lyrics.put(query, result); + state + .data + .write() + .caches + .lyrics + .insert(query, result, *CACHE_DURATION); } } ClientRequest::ConnectDevice(id) => { @@ -255,44 +260,47 @@ impl Client { } ClientRequest::GetUserTopTracks => { let uri = &USER_TOP_TRACKS_ID.uri; - if !state.data.read().caches.context.contains(uri) { + if !state.data.read().caches.context.contains_key(uri) { let tracks = self.current_user_top_tracks().await?; - state.data.write().caches.context.put( + state.data.write().caches.context.insert( uri.to_owned(), Context::Tracks { tracks, desc: "User's top tracks".to_string(), }, + *CACHE_DURATION, ); } } ClientRequest::GetUserSavedTracks => { let tracks = self.current_user_saved_tracks().await?; - state.data.write().caches.context.put( + state.data.write().caches.context.insert( USER_LIKED_TRACKS_ID.uri.to_owned(), Context::Tracks { tracks: tracks.clone(), desc: "User's liked tracks".to_string(), }, + *CACHE_DURATION, ); state.data.write().user_data.saved_tracks = tracks; } ClientRequest::GetUserRecentlyPlayedTracks => { let uri = &USER_RECENTLY_PLAYED_TRACKS_ID.uri; - if !state.data.read().caches.context.contains(uri) { + if !state.data.read().caches.context.contains_key(uri) { let tracks = self.current_user_recently_played_tracks().await?; - state.data.write().caches.context.put( + state.data.write().caches.context.insert( uri.to_owned(), Context::Tracks { tracks, desc: "User's recently played tracks".to_string(), }, + *CACHE_DURATION, ); } } ClientRequest::GetContext(context) => { let uri = context.uri(); - if !state.data.read().caches.context.contains(&uri) { + if !state.data.read().caches.context.contains_key(&uri) { let context = match context { ContextId::Playlist(playlist_id) => { self.playlist_context(playlist_id).await? @@ -306,14 +314,24 @@ impl Client { } }; - state.data.write().caches.context.put(uri, context); + state + .data + .write() + .caches + .context + .insert(uri, context, *CACHE_DURATION); } } ClientRequest::Search(query) => { - if !state.data.read().caches.search.contains(&query) { + if !state.data.read().caches.search.contains_key(&query) { let results = self.search(&query).await?; - state.data.write().caches.search.put(query, results); + state + .data + .write() + .caches + .search + .insert(query, results, *CACHE_DURATION); } } ClientRequest::GetRadioTracks { @@ -321,15 +339,16 @@ impl Client { seed_name: name, } => { let radio_uri = format!("radio:{uri}"); - if !state.data.read().caches.context.contains(&radio_uri) { + if !state.data.read().caches.context.contains_key(&radio_uri) { let tracks = self.radio_tracks(uri).await?; - state.data.write().caches.context.put( + state.data.write().caches.context.insert( radio_uri, Context::Tracks { tracks, desc: format!("{name} Radio"), }, + *CACHE_DURATION, ); } } @@ -812,7 +831,7 @@ impl Client { .await?; // After adding a new track to a playlist, remove the cache of that playlist to force refetching new data - state.data.write().caches.context.pop(&playlist_id.uri()); + state.data.write().caches.context.remove(&playlist_id.uri()); Ok(()) } @@ -1185,13 +1204,18 @@ impl Client { } #[cfg(feature = "image")] - if !state.data.read().caches.images.contains(url) { + if !state.data.read().caches.images.contains_key(url) { let bytes = self.retrieve_image(url, &path, false).await?; // Get the image from a url let image = image::load_from_memory(&bytes).context("Failed to load image from memory")?; - state.data.write().caches.images.put(url.to_owned(), image); + state + .data + .write() + .caches + .images + .insert(url.to_owned(), image, *CACHE_DURATION); } // notify user about the playback's change if any diff --git a/spotify_player/src/event/page.rs b/spotify_player/src/event/page.rs index 0160b018..a1c5f33f 100644 --- a/spotify_player/src/event/page.rs +++ b/spotify_player/src/event/page.rs @@ -152,7 +152,7 @@ pub fn handle_key_sequence_for_search_page( }; let data = state.data.read(); - let search_results = data.caches.search.peek(current_query); + let search_results = data.caches.search.get(current_query); match focus_state { SearchFocusState::Input => anyhow::bail!("user's search input should be handled before"), diff --git a/spotify_player/src/event/window.rs b/spotify_player/src/event/window.rs index 3350de1d..4a1faf36 100644 --- a/spotify_player/src/event/window.rs +++ b/spotify_player/src/event/window.rs @@ -55,7 +55,7 @@ pub fn handle_command_for_focused_context_window( let data = state.data.read(); - match data.caches.context.peek(&context_id.uri()) { + match data.caches.context.get(&context_id.uri()) { Some(context) => match context { Context::Artist { top_tracks, diff --git a/spotify_player/src/state/data.rs b/spotify_player/src/state/data.rs index 14413b16..2bf96999 100644 --- a/spotify_player/src/state/data.rs +++ b/spotify_player/src/state/data.rs @@ -1,10 +1,16 @@ -use std::{collections::HashMap, num::NonZeroUsize}; +use std::collections::HashMap; + +use once_cell::sync::Lazy; use super::model::*; pub type DataReadGuard<'a> = parking_lot::RwLockReadGuard<'a, AppData>; -#[derive(Default, Debug)] +// cache duration, which is default to be 1h +pub static CACHE_DURATION: Lazy = + Lazy::new(|| std::time::Duration::from_secs(60 * 60)); + +#[derive(Default)] /// the application's data pub struct AppData { pub user_data: UserData, @@ -22,15 +28,14 @@ pub struct UserData { pub saved_tracks: Vec, } -#[derive(Debug)] /// the application's caches pub struct Caches { - pub context: lru::LruCache, - pub search: lru::LruCache, + pub context: ttl_cache::TtlCache, + pub search: ttl_cache::TtlCache, #[cfg(feature = "lyric-finder")] - pub lyrics: lru::LruCache, + pub lyrics: ttl_cache::TtlCache, #[cfg(feature = "image")] - pub images: lru::LruCache, + pub images: ttl_cache::TtlCache, } #[derive(Default, Debug)] @@ -42,19 +47,19 @@ pub struct BrowseData { impl Default for Caches { fn default() -> Self { Self { - context: lru::LruCache::new(NonZeroUsize::new(64).unwrap()), - search: lru::LruCache::new(NonZeroUsize::new(64).unwrap()), + context: ttl_cache::TtlCache::new(64), + search: ttl_cache::TtlCache::new(64), #[cfg(feature = "lyric-finder")] - lyrics: lru::LruCache::new(NonZeroUsize::new(64).unwrap()), + lyrics: ttl_cache::TtlCache::new(64), #[cfg(feature = "image")] - images: lru::LruCache::new(NonZeroUsize::new(64).unwrap()), + images: ttl_cache::TtlCache::new(64), } } } impl AppData { pub fn get_tracks_by_id_mut(&mut self, id: &ContextId) -> Option<&mut Vec> { - self.caches.context.peek_mut(&id.uri()).map(|c| match c { + self.caches.context.get_mut(&id.uri()).map(|c| match c { Context::Album { tracks, .. } => tracks, Context::Playlist { tracks, .. } => tracks, Context::Artist { diff --git a/spotify_player/src/state/mod.rs b/spotify_player/src/state/mod.rs index 6bc659dc..a2cf6f71 100644 --- a/spotify_player/src/state/mod.rs +++ b/spotify_player/src/state/mod.rs @@ -19,7 +19,6 @@ pub use parking_lot::{Mutex, RwLock}; pub type SharedState = std::sync::Arc; /// Application's state -#[derive(Debug)] pub struct State { pub app_config: config::AppConfig, pub keymap_config: config::KeymapConfig, diff --git a/spotify_player/src/ui/page.rs b/spotify_player/src/ui/page.rs index 1620a48a..b6756e05 100644 --- a/spotify_player/src/ui/page.rs +++ b/spotify_player/src/ui/page.rs @@ -25,7 +25,7 @@ pub fn render_search_page( s => anyhow::bail!("expect a search page state, found {s:?}"), }; - let search_results = data.caches.search.peek(current_query); + let search_results = data.caches.search.get(current_query); // 2. Construct the page's layout let rect = construct_and_render_block("Search", &ui.theme, state, Borders::ALL, frame, rect); @@ -229,7 +229,7 @@ pub fn render_context_page( }; let data = state.data.read(); - match data.caches.context.peek(&id.uri()) { + match data.caches.context.get(&id.uri()) { Some(context) => { // render context description let chunks = Layout::default() @@ -511,7 +511,7 @@ pub fn render_lyric_page( s => anyhow::bail!("expect a lyric page state, found {s:?}"), }; - let (desc, lyric) = match data.caches.lyrics.peek(&format!("{track} {artists}")) { + let (desc, lyric) = match data.caches.lyrics.get(&format!("{track} {artists}")) { None => { frame.render_widget(Paragraph::new("Loading..."), rect); return Ok(()); diff --git a/spotify_player/src/ui/playback.rs b/spotify_player/src/ui/playback.rs index 7d648e74..b80f85d2 100644 --- a/spotify_player/src/ui/playback.rs +++ b/spotify_player/src/ui/playback.rs @@ -275,7 +275,7 @@ fn render_playback_cover_image( } let data = state.data.read(); - if let Some(image) = data.caches.images.peek(&url) { + if let Some(image) = data.caches.images.get(&url) { ui.last_cover_image_render_info = Some((url, std::time::Instant::now())); // `viuer` renders image using `sixel` in a different scale compared to other methods.