mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-04 12:13:27 +00:00
[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:
parent
6fa8a124c8
commit
7ace99cf1c
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<_>>();
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
Loading…
Reference in a new issue