diff --git a/src/cache.rs b/src/cache.rs index 8ae67f0..8439f04 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -9,6 +9,11 @@ macro_rules! use_api_cache { return Ok(serde_json::from_str(&ret).unwrap()); } }; + ($route:literal, $id:literal, $cache:ident) => { + if let Some(ret) = $cache.get_only($route, $id).await { + return Ok(serde_json::from_str(&ret).unwrap()); + } + }; } pub struct RouteCache { diff --git a/src/library/mod.rs b/src/library/mod.rs index 39e3e48..ead4cb7 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -8,6 +8,8 @@ use serde_json::json; use track::Track; use walkdir::WalkDir; +use crate::cache::RouteCache; + pub mod album; pub mod artist; pub mod metadata; @@ -164,7 +166,9 @@ impl Libary { Track::get(track_id).await } - pub async fn rescan(&self) { + pub async fn rescan(&self, cache: &RouteCache) { + cache.invalidate("albums", "latest").await; + log::info!("Rescanning library"); for entry in WalkDir::new(self.root_dir.clone()) .follow_links(true) diff --git a/src/main.rs b/src/main.rs index 3e10471..c771f0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,8 +28,9 @@ async fn rocket() -> _ { .expect("error creating CORS options"); let lib = Libary::new("./media".into()); + let cache = cache::RouteCache::new(); - lib.rescan().await; + lib.rescan(&cache).await; // create initial admin user if User::find(doc! { "username": "admin" }, None) @@ -39,8 +40,6 @@ async fn rocket() -> _ { User::create("admin", "admin", UserRole::Admin).await; } - let cache = cache::RouteCache::new(); - rocket::build() .mount( "/", @@ -52,6 +51,7 @@ async fn rocket() -> _ { route::artist::artist_image_route, route::album::albums_route, route::album::album_route, + route::album::latest_albums_route, route::track::track_route, route::track::track_audio_route, route::album::album_cover_route, diff --git a/src/route/album.rs b/src/route/album.rs index 00eb0a7..076de38 100644 --- a/src/route/album.rs +++ b/src/route/album.rs @@ -3,6 +3,8 @@ use std::cmp::Ordering; use super::api_error; use super::FallibleApiResponse; use super::ToAPI; +use mongod::Model; +use mongod::Referencable; use mongodb::bson::doc; use rocket::fs::NamedFile; use rocket::{get, State}; @@ -10,6 +12,7 @@ use serde_json::json; use crate::cache::RouteCache; use crate::library::album::Album; +use crate::library::track::Track; use crate::library::Libary; use crate::route::to_api; use crate::use_api_cache; @@ -55,6 +58,39 @@ pub async fn album_cover_route( .ok() } +#[get("/albums/latest")] +pub async fn latest_albums_route(cache: &State) -> FallibleApiResponse { + use_api_cache!("albums", "latest", cache); + + let albums = Album::find(doc! {}, None).await.unwrap(); + + let mut albums_tracks = vec![]; + + for album in &albums { + albums_tracks.push( + Track::find_one(doc! { "album_id": album.reference()}) + .await + .unwrap(), + ); + } + + let mut joined: Vec<(_, _)> = albums.into_iter().zip(albums_tracks).collect(); + + joined.sort_by(|a, b| a.1.date_added.cmp(&b.1.date_added)); + + joined.reverse(); + + let (albums, _): (Vec<_>, Vec<_>) = joined.into_iter().unzip(); + + let albums = to_api(&albums).await; + + cache + .insert("albums", "latest", serde_json::to_string(&albums).unwrap()) + .await; + + Ok(json!(albums)) +} + #[get("/album/")] pub async fn album_route( album_id: &str,