diff --git a/Cargo.lock b/Cargo.lock index 54d6f26..4f587ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1354,6 +1354,7 @@ dependencies = [ "notify", "path-absolutize", "prometheus", + "rand", "rayon", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 70aa5dc..39e32af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,3 +37,4 @@ actix-web-prom = "0.9.0" prometheus = "0.13" lazy_static = "1.5" anyhow = "1.0" +rand = "0.8.5" diff --git a/src/data/mod.rs b/src/data/mod.rs index 55e053a..7ac194a 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -100,6 +100,14 @@ pub struct PhotosResponse { pub dirs: Vec, } +#[derive(Copy, Clone, Deserialize, PartialEq, Debug)] +#[serde(rename_all = "lowercase")] +pub enum SortType { + Shuffle, + NameAsc, + NameDesc, +} + #[derive(Deserialize)] pub struct FilesRequest { pub path: String, @@ -107,6 +115,7 @@ pub struct FilesRequest { pub tag_ids: Option, pub tag_filter_mode: Option, pub recursive: Option, + pub sort: Option, } #[derive(Copy, Clone, Deserialize, PartialEq, Debug)] diff --git a/src/files.rs b/src/files.rs index 1e97aa8..40ba3c3 100644 --- a/src/files.rs +++ b/src/files.rs @@ -16,13 +16,15 @@ use actix_web::{ }; use log::{debug, error, info, trace}; -use crate::data::{Claims, FilesRequest, FilterMode, PhotosResponse}; +use crate::data::{Claims, FilesRequest, FilterMode, PhotosResponse, SortType}; use crate::{create_thumbnails, AppState}; use crate::error::IntoHttpError; use crate::tags::TagDao; use crate::video::StreamActor; use path_absolutize::*; +use rand::prelude::SliceRandom; +use rand::{random, thread_rng}; use serde::Deserialize; pub async fn list_photos( @@ -52,6 +54,14 @@ pub async fn list_photos( return dao .get_files_with_any_tag_ids(tag_ids.clone()) .context(format!("Failed to get files with tag_ids: {:?}", tag_ids)) + .map(|mut tagged_files| { + return if let Some(sort_type) = req.sort { + debug!("Sorting files: {:?}", sort_type); + sort(tagged_files, sort_type) + } else { + tagged_files + }; + }) .map(|tagged_files| match filter_mode { FilterMode::Any => tagged_files .iter() @@ -89,7 +99,7 @@ pub async fn list_photos( if let Ok(files) = file_system.get_files_for_path(search_path) { debug!("Valid search path: {:?}", search_path); - let photos = files + let mut photos = files .iter() .filter(|&f| { f.metadata().map_or_else( @@ -127,6 +137,11 @@ pub async fn list_photos( }) .collect::>(); + if let Some(sort_type) = req.sort { + debug!("Sorting files: {:?}", sort_type); + photos = sort(photos, sort_type) + } + let dirs = files .iter() .filter(|&f| f.metadata().map_or(false, |md| md.is_dir())) @@ -144,6 +159,20 @@ pub async fn list_photos( } } +fn sort(mut files: Vec, sort_type: SortType) -> Vec { + match sort_type { + SortType::Shuffle => files.shuffle(&mut thread_rng()), + SortType::NameAsc => { + files.sort_by(|l, r| l.cmp(&r)); + } + SortType::NameDesc => { + files.sort_by(|l, r| r.cmp(&l)); + } + } + + files +} + pub fn list_files(dir: &Path) -> io::Result> { let files = read_dir(dir)? .filter_map(|res| res.ok()) @@ -425,7 +454,7 @@ mod tests { let body: PhotosResponse = serde_json::from_str(&response.read_to_str()).unwrap(); debug!("{:?}", body); - + assert!(body.photos.contains(&String::from("photo.jpg"))); assert!(body.dirs.contains(&String::from("test-dir"))); assert!(body