[ENG-1063] Actually regenerate thumbnails on Regen Thumbnails button (#1288)

Done

Co-authored-by: jake <77554505+brxken128@users.noreply.github.com>
This commit is contained in:
Ericson "Fogo" Soares 2023-09-02 20:15:41 -03:00 committed by GitHub
parent 6fa8a124c8
commit 7ace99cf1c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 118 additions and 50 deletions

View file

@ -227,17 +227,25 @@ pub(crate) fn mount() -> AlphaRouter<Ctx> {
pub struct GenerateThumbsForLocationArgs {
pub id: location::id::Type,
pub path: PathBuf,
#[serde(default)]
pub regenerate: bool,
}
R.with2(library()).mutation(
|(node, library), args: GenerateThumbsForLocationArgs| async move {
let Some(location) = find_location(&library, args.id).exec().await? else {
return Err(LocationError::IdNotFound(args.id).into());
|(node, library),
GenerateThumbsForLocationArgs {
id,
path,
regenerate,
}: GenerateThumbsForLocationArgs| async move {
let Some(location) = find_location(&library, id).exec().await? else {
return Err(LocationError::IdNotFound(id).into());
};
Job::new(MediaProcessorJobInit {
location,
sub_path: Some(args.path),
sub_path: Some(path),
regenerate_thumbnails: regenerate,
})
.spawn(&node, &library)
.await

View file

@ -412,6 +412,7 @@ pub async fn scan_location(
.queue_next(MediaProcessorJobInit {
location: location_base_data,
sub_path: None,
regenerate_thumbnails: false,
})
.spawn(node, library)
.await
@ -450,6 +451,7 @@ pub async fn scan_location_sub_path(
.queue_next(MediaProcessorJobInit {
location: location_base_data,
sub_path: Some(sub_path),
regenerate_thumbnails: false,
})
.spawn(node, library)
.await

View file

@ -88,7 +88,15 @@ pub async fn process(
)])
.select(media_data::select!({ object_id }))
.exec()
.await?
.await?;
if files_paths.len() == objects_already_with_media_data.len() {
// All files already have media data, skipping
run_metadata.skipped = files_paths.len() as u32;
return Ok((run_metadata, JobRunErrors::default()));
}
let objects_already_with_media_data = objects_already_with_media_data
.into_iter()
.map(|media_data| media_data.object_id)
.collect::<HashSet<_>>();

View file

@ -37,6 +37,7 @@ const BATCH_SIZE: usize = 10;
pub struct MediaProcessorJobInit {
pub location: location::Data,
pub sub_path: Option<PathBuf>,
pub regenerate_thumbnails: bool,
}
impl Hash for MediaProcessorJobInit {
@ -181,6 +182,7 @@ impl StatefulJob for MediaProcessorJobInit {
self.location.id,
&data.location_path,
&data.thumbnails_base_dir,
self.regenerate_thumbnails,
&ctx.library,
|completed_count| {
ctx.progress(vec![JobReportUpdate::CompletedTaskCount(

View file

@ -120,6 +120,7 @@ async fn process(
location_id: location::id::Type,
location_path: impl AsRef<Path>,
thumbnails_base_dir: impl AsRef<Path>,
regenerate_thumbnails: bool,
library: &Library,
ctx_update_fn: impl Fn(usize),
) -> Result<(MediaProcessorMetadata, JobRunErrors), MediaProcessorError> {
@ -168,6 +169,7 @@ async fn process(
location_id,
location_path,
thumbnails_base_dir,
regenerate_thumbnails,
library,
ctx_update_fn,
)

View file

@ -124,6 +124,7 @@ pub async fn shallow(
location.id,
&location_path,
&thumbnails_base_dir,
false,
library,
|_| {},
)
@ -135,7 +136,7 @@ pub async fn shallow(
}
}
debug!("Media shallow processor run metadata: {run_metadata:#?}");
debug!("Media shallow processor run metadata: {run_metadata:?}");
if run_metadata.media_data.extracted > 0 || run_metadata.thumbnailer.created > 0 {
invalidate_query!(library, "search.paths");

View file

@ -258,6 +258,7 @@ pub(super) async fn process(
location_id: location::id::Type,
location_path: impl AsRef<Path>,
thumbnails_base_dir: impl AsRef<Path>,
regenerate: bool,
library: &Library,
ctx_update_fn: impl Fn(usize),
) -> Result<(ThumbnailerMetadata, JobRunErrors), ThumbnailerError> {
@ -354,12 +355,29 @@ pub(super) async fn process(
ctx_update_fn(idx + 1);
match metadata_res {
Ok(_) => {
trace!(
"Thumb already exists, skipping generation for {}",
output_path.display()
);
run_metadata.skipped += 1;
continue;
if !regenerate {
trace!(
"Thumbnail already exists, skipping generation for {}",
input_path.display()
);
run_metadata.skipped += 1;
} else {
tracing::debug!(
"Renegerating thumbnail {} to {}",
input_path.display(),
output_path.display()
);
process_single_thumbnail(
cas_id,
kind,
&input_path,
&output_path,
&mut errors,
&mut run_metadata,
library,
)
.await;
}
}
Err(e) if e.kind() == io::ErrorKind::NotFound => {
@ -369,41 +387,16 @@ pub(super) async fn process(
output_path.display()
);
match kind {
ThumbnailerEntryKind::Image => {
if let Err(e) = generate_image_thumbnail(&input_path, &output_path).await {
error!(
"Error generating thumb for image \"{}\": {e:#?}",
input_path.display()
);
errors.push(format!(
"Had an error generating thumbnail for \"{}\"",
input_path.display()
));
continue;
}
}
#[cfg(feature = "ffmpeg")]
ThumbnailerEntryKind::Video => {
if let Err(e) = generate_video_thumbnail(&input_path, &output_path).await {
error!(
"Error generating thumb for video \"{}\": {e:#?}",
input_path.display()
);
errors.push(format!(
"Had an error generating thumbnail for \"{}\"",
input_path.display()
));
continue;
}
}
}
trace!("Emitting new thumbnail event");
library.emit(CoreEvent::NewThumbnail {
thumb_key: get_thumb_key(cas_id),
});
run_metadata.created += 1;
process_single_thumbnail(
cas_id,
kind,
&input_path,
&output_path,
&mut errors,
&mut run_metadata,
library,
)
.await;
}
Err(e) => {
error!(
@ -420,3 +413,53 @@ pub(super) async fn process(
Ok((run_metadata, errors.into()))
}
// Using &Path as this function if private only to this module, always being used with a &Path, so we
// don't pay the compile price for generics
async fn process_single_thumbnail(
cas_id: &str,
kind: ThumbnailerEntryKind,
input_path: &Path,
output_path: &Path,
errors: &mut Vec<String>,
run_metadata: &mut ThumbnailerMetadata,
library: &Library,
) {
match kind {
ThumbnailerEntryKind::Image => {
if let Err(e) = generate_image_thumbnail(&input_path, &output_path).await {
error!(
"Error generating thumb for image \"{}\": {e:#?}",
input_path.display()
);
errors.push(format!(
"Had an error generating thumbnail for \"{}\"",
input_path.display()
));
return;
}
}
#[cfg(feature = "ffmpeg")]
ThumbnailerEntryKind::Video => {
if let Err(e) = generate_video_thumbnail(&input_path, &output_path).await {
error!(
"Error generating thumb for video \"{}\": {e:#?}",
input_path.display()
);
errors.push(format!(
"Had an error generating thumbnail for \"{}\"",
input_path.display()
));
return;
}
}
}
trace!("Emitting new thumbnail event");
library.emit(CoreEvent::NewThumbnail {
thumb_key: get_thumb_key(cas_id),
});
run_metadata.created += 1;
}

View file

@ -208,7 +208,8 @@ export const ParentFolderActions = new ConditionalItem({
try {
await generateThumbnails.mutateAsync({
id: parent.location.id,
path: selectedFilePaths[0]?.materialized_path ?? '/'
path: selectedFilePaths[0]?.materialized_path ?? '/',
regenerate: true,
});
} catch (error) {
toast.error({

View file

@ -129,7 +129,8 @@ export default (props: PropsWithChildren) => {
try {
await generateThumbsForLocation.mutateAsync({
id: parent.location.id,
path: currentPath ?? '/'
path: currentPath ?? '/',
regenerate: true,
});
} catch (error) {
toast.error({

View file

@ -177,7 +177,7 @@ export type FromPattern = { pattern: string; replace_all: boolean }
export type FullRescanArgs = { location_id: number; reidentify_objects: boolean }
export type GenerateThumbsForLocationArgs = { id: number; path: string }
export type GenerateThumbsForLocationArgs = { id: number; path: string; regenerate?: boolean }
export type GetAll = { backups: Backup[]; directory: string }