From 4f327db611ffacc64201a504b26e06af105c2bb3 Mon Sep 17 00:00:00 2001 From: jake <77554505+brxken128@users.noreply.github.com> Date: Fri, 5 May 2023 23:01:34 +0100 Subject: [PATCH] source `has_thumbnail` correctly for the recent file query (#797) * source `has_thumbnail` correctly * correctly check if thumbnail exists in watcher * regen bindings * add helper fn for getting thumbnail directory and cleanup old thumbnails if the watcher creates a new one --- core/src/api/files.rs | 25 ++++++++++++++++------ core/src/library/library.rs | 9 ++------ core/src/location/manager/watcher/utils.rs | 16 ++++++-------- core/src/object/preview/thumbnail/mod.rs | 17 ++++++++++++--- packages/client/src/core.ts | 4 ++-- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/core/src/api/files.rs b/core/src/api/files.rs index 9619ef564..7560e0877 100644 --- a/core/src/api/files.rs +++ b/core/src/api/files.rs @@ -148,13 +148,24 @@ pub(crate) fn mount() -> AlphaRouter { .exec() .await?; - Ok(file_paths - .into_iter() - .map(|x| ExplorerItem::Path { - has_thumbnail: x.object.clone().map_or(false, |x| x.has_thumbnail), - item: x, - }) - .collect::>()) + let mut items = vec![]; + + for path in file_paths.into_iter() { + let has_thumbnail = if let Some(cas_id) = &path.cas_id { + library.thumbnail_exists(cas_id).await.map_err(|e| { + rspc::Error::new(ErrorCode::InternalServerError, e.to_string()) + })? + } else { + false + }; + + items.push(ExplorerItem::Path { + has_thumbnail, + item: path, + }); + } + + Ok(items) }) }) .procedure("encryptFiles", { diff --git a/core/src/library/library.rs b/core/src/library/library.rs index de2575541..4d89ce761 100644 --- a/core/src/library/library.rs +++ b/core/src/library/library.rs @@ -3,7 +3,7 @@ use crate::{ job::{IntoJob, JobInitData, JobManagerError, StatefulJob}, location::{file_path_helper::MaterializedPath, LocationManager}, node::NodeConfigManager, - object::preview::THUMBNAIL_CACHE_DIR_NAME, + object::preview::get_thumbnail_path, prisma::{file_path, location, PrismaClient}, sync::SyncManager, NodeContext, @@ -85,12 +85,7 @@ impl Library { } pub async fn thumbnail_exists(&self, cas_id: &str) -> tokio::io::Result { - let thumb_path = self - .config() - .data_directory() - .join(THUMBNAIL_CACHE_DIR_NAME) - .join(cas_id) - .with_extension("webp"); + let thumb_path = get_thumbnail_path(self, cas_id); match tokio::fs::metadata(thumb_path).await { Ok(_) => Ok(true), diff --git a/core/src/location/manager/watcher/utils.rs b/core/src/location/manager/watcher/utils.rs index 79e9ea0b3..750add6ed 100644 --- a/core/src/location/manager/watcher/utils.rs +++ b/core/src/location/manager/watcher/utils.rs @@ -16,9 +16,7 @@ use crate::{ object::{ file_identifier::FileMetadata, object_just_id_has_thumbnail, - preview::{ - can_generate_thumbnail_for_image, generate_image_thumbnail, THUMBNAIL_CACHE_DIR_NAME, - }, + preview::{can_generate_thumbnail_for_image, generate_image_thumbnail, get_thumbnail_path}, validation::hash::file_checksum, }, prisma::{file_path, location, object}, @@ -422,8 +420,11 @@ async fn inner_update_file( if let Some(ref object) = file_path.object { // if this file had a thumbnail previously, we update it to match the new content - if object.has_thumbnail && !file_path.extension.is_empty() { + if library.thumbnail_exists(old_cas_id).await? && !file_path.extension.is_empty() { generate_thumbnail(&file_path.extension, &cas_id, full_path, library).await; + + // remove the old thumbnail as we're generating a new one + fs::remove_file(get_thumbnail_path(library, old_cas_id)).await?; } let int_kind = kind as i32; @@ -657,12 +658,7 @@ async fn generate_thumbnail( library: &Library, ) { let path = path.as_ref(); - let output_path = library - .config() - .data_directory() - .join(THUMBNAIL_CACHE_DIR_NAME) - .join(cas_id) - .with_extension("webp"); + let output_path = get_thumbnail_path(library, cas_id); if let Err(e) = fs::metadata(&output_path).await { if e.kind() != ErrorKind::NotFound { diff --git a/core/src/object/preview/thumbnail/mod.rs b/core/src/object/preview/thumbnail/mod.rs index b5796e740..a0b493a20 100644 --- a/core/src/object/preview/thumbnail/mod.rs +++ b/core/src/object/preview/thumbnail/mod.rs @@ -2,6 +2,7 @@ use crate::{ api::CoreEvent, invalidate_query, job::{JobError, JobReportUpdate, JobResult, WorkerContext}, + library::Library, location::{ file_path_helper::{ file_path_just_materialized_path_cas_id, FilePathError, MaterializedPath, @@ -32,9 +33,19 @@ use webp::Encoder; pub mod shallow_thumbnailer_job; pub mod thumbnailer_job; -static THUMBNAIL_SIZE_FACTOR: f32 = 0.2; -static THUMBNAIL_QUALITY: f32 = 30.0; -pub static THUMBNAIL_CACHE_DIR_NAME: &str = "thumbnails"; +const THUMBNAIL_SIZE_FACTOR: f32 = 0.2; +const THUMBNAIL_QUALITY: f32 = 30.0; +pub const THUMBNAIL_CACHE_DIR_NAME: &str = "thumbnails"; + +/// This does not check if a thumbnail exists, it just returns the path that it would exist at +pub fn get_thumbnail_path(library: &Library, cas_id: &str) -> PathBuf { + library + .config() + .data_directory() + .join(THUMBNAIL_CACHE_DIR_NAME) + .join(cas_id) + .with_extension("webp") +} #[cfg(feature = "ffmpeg")] static FILTERED_VIDEO_EXTENSIONS: Lazy> = Lazy::new(|| { diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts index f81680a92..fc9d3d84e 100644 --- a/packages/client/src/core.ts +++ b/packages/client/src/core.ts @@ -224,8 +224,6 @@ export type LocationExplorerArgs = { location_id: number; path?: string | null; export type TagAssignArgs = { object_id: number; tag_id: number; unassign: boolean } -export type RenameFileArgs = { location_id: number; file_name: string; new_file_name: string } - export type FileDeleterJobInit = { location_id: number; path_id: number } /** @@ -282,6 +280,8 @@ export type LibraryArgs = { library_id: string; arg: T } export type IdentifyUniqueFilesArgs = { id: number; path: string } +export type RenameFileArgs = { location_id: number; file_name: string; new_file_name: string } + export type ExplorerContext = ({ type: "Location" } & Location) | ({ type: "Tag" } & Tag) export type OwnedOperationData = { Create: { [key: string]: any } } | { CreateMany: { values: ([any, { [key: string]: any }])[]; skip_duplicates: boolean } } | { Update: { [key: string]: any } } | "Delete"