diff --git a/crates/engine/src/lightroom_catalog.rs b/crates/engine/src/lightroom_catalog.rs index 3066187ce..e29a9e71a 100644 --- a/crates/engine/src/lightroom_catalog.rs +++ b/crates/engine/src/lightroom_catalog.rs @@ -956,6 +956,9 @@ pub(crate) fn apply_prepared(s: &mut crate::Session, mut data: CatalogImport, co let mut promoted = HashSet::new(); let mut unmapped = BTreeMap::new(); let mut preserved = 0; + // Library photos in Recently Deleted that a catalog photo matched: they stay there (only an + // import of the files themselves brings a photo back, see `OnDeleted`), and the report says so. + let mut in_trash = HashSet::new(); // Masters precede their virtual copies. let mut photos: Vec<_> = data.photos.iter().collect(); photos.sort_by_key(|p| number(&p.image, "masterImage") != 0); @@ -980,6 +983,9 @@ pub(crate) fn apply_prepared(s: &mut crate::Session, mut data: CatalogImport, co .filter(|id| s.catalog.photo(*id).is_some_and(|p| source_path(p).is_some_and(|path| key(path) == key(&src.path)))); let existing_id = indexed.or_else(|| if master == 0 { by_path.get(&key(&src.path)).copied() } else { None }); let mut p = if let Some(id) = existing_id.and_then(|id| s.catalog.photo(id)) { + if id.deleted { + in_trash.insert(id.id); + } (**id).clone() } else if master != 0 { let master_id = ids.get(&master).copied().ok_or_else(|| error(format!("virtual copy {}: missing master", src.source_id)))?; @@ -991,12 +997,21 @@ pub(crate) fn apply_prepared(s: &mut crate::Session, mut data: CatalogImport, co p.id = s.catalog.alloc_photo_id(); p.copy_of = Some(master_id); p.copy_name = Some(text(&src.image, "copyName").to_string()); + // a new copy of a master in Recently Deleted is imported, not trashed with it + p.deleted = false; p } else if let Some(duplicate) = duplicates.get(&key(&src.path)).and_then(|id| s.catalog.photo(*id)) { // Catalogs can contain distinct originals with identical bytes and independent edits. + // The clone is a new record of its own: never in Recently Deleted because the photo with + // the same bytes is (#2589), nor a virtual copy because that photo is one. let mut p = (**duplicate).clone(); p.id = s.catalog.alloc_photo_id(); p.source = Source::File { path: src.path.clone() }; + p.deleted = false; + p.copy_of = None; + p.copy_name = None; + p.local_baseline = None; + p.imported = now.clone(); p.file_name = Path::new(&src.path).file_name().map(|n| n.to_string_lossy().into()).unwrap_or_else(|| src.path.clone()); p } else { @@ -1105,7 +1120,7 @@ pub(crate) fn apply_prepared(s: &mut crate::Session, mut data: CatalogImport, co } s.merge_undo(s.undo.len().saturating_sub(undo0), "Import Lightroom Catalog"); Ok(AppliedImport { - report: json!({"source":data.source,"photos":ids.len(),"imported":imported,"library":s.library.as_ref().filter(|l| l.on_disk).map(|l| l.dir.to_string_lossy()),"collections":album_count,"preservedExistingEdits":preserved,"missing":missing,"failed":files.failed,"warnings":warnings,"skipped":data.skipped,"unmapped":unmapped,"archive":archive_path,"mapping":ids}), + report: json!({"source":data.source,"photos":ids.len(),"imported":imported,"library":s.library.as_ref().filter(|l| l.on_disk).map(|l| l.dir.to_string_lossy()),"collections":album_count,"preservedExistingEdits":preserved,"inRecentlyDeleted":in_trash.len(),"missing":missing,"failed":files.failed,"warnings":warnings,"skipped":data.skipped,"unmapped":unmapped,"archive":archive_path,"mapping":ids}), index: index.clone(), }) } diff --git a/crates/engine/src/lightroom_catalog/tests.rs b/crates/engine/src/lightroom_catalog/tests.rs index 654b40458..96a6fca4e 100644 --- a/crates/engine/src/lightroom_catalog/tests.rs +++ b/crates/engine/src/lightroom_catalog/tests.rs @@ -157,6 +157,82 @@ fn imports_copies_collections_ratings_and_mapped_edits_in_one_undo_step() { assert!(s.catalog.albums().next().is_none()); assert!(data.photos[1].settings.contains("CameraProfile")); // unmapped source stays intact } +/// A library photo at `path`, already committed. +fn library_photo(s: &mut crate::Session, path: &str, edit: impl FnOnce(&mut Photo)) -> PhotoId { + let id = s.catalog.alloc_photo_id(); + let mut p = Photo::new(id, Source::File { path: path.into() }, "a.ARW", "ARW", 20, 10, "before"); + p.content_hash = Some("same-bytes".into()); + edit(&mut p); + s.commit("Add", Op::AddPhoto { photo: Box::new(p) }).unwrap(); + id +} +/// A catalog photo whose file has the same bytes as a library photo in Recently Deleted (or a +/// virtual copy) is a new record of its own: it was counted as imported but landed in Recently +/// Deleted (or became a "copy" of nothing it was copied from), so a large import into a library +/// with a trash went missing photo by photo (#2589). +#[test] +fn a_content_duplicate_of_a_trashed_photo_or_virtual_copy_is_a_new_live_photo() { + let trashed: fn(&mut Photo) = |p| p.deleted = true; + let virtual_copy: fn(&mut Photo) = |p| { + p.copy_of = Some(PhotoId(999)); + p.copy_name = Some("Copy 1".into()); + }; + for (case, edit) in [("trashed", trashed), ("virtual copy", virtual_copy)] { + let mut s = crate::Session::new(); + let other = library_photo(&mut s, "/elsewhere/a.ARW", edit); + let mut data = sample(); + data.photos.retain(|p| number(&p.image, "masterImage") == 0); + let files = crate::import::ImportReport { + duplicates: vec![crate::import::Duplicate { + path: "/missing/test.ARW".into(), + existing: Some(other.0), + reason: "content", + existing_deleted: case == "trashed", + }], + ..Default::default() + }; + let undo0 = s.undo.len(); + let r = apply_prepared( + &mut s, + data, + ApplyPrepared { + update_existing: false, + files, + index: &mut ImportIndex::default(), + archive_path: None, + missing_sources: &HashSet::new(), + undo0, + }, + ) + .unwrap() + .report; + assert_eq!(r["imported"], 1, "{case}: {r}"); + assert_eq!(r["inRecentlyDeleted"], 0, "{case}: {r}"); + let new = s.catalog.photo(PhotoId(r["mapping"]["1"].as_u64().unwrap())).unwrap(); + assert_ne!(new.id, other, "{case}"); + assert!(!new.deleted, "{case}: the new record must not be in Recently Deleted"); + assert_eq!((new.copy_of, new.copy_name.as_deref()), (None, None), "{case}: the new record is no virtual copy"); + assert_ne!(new.imported, "before", "{case}: imported now, not when the other photo was"); + assert_eq!(new.rating, 5, "{case}: the catalog's own metadata applies"); + assert_eq!(s.catalog.photo(other).unwrap().deleted, case == "trashed", "{case}: the other photo is left as it was"); + } +} +/// A catalog photo matched (by path) to a library photo in Recently Deleted leaves it there, as +/// the import was set up to do (a Lightroom import doesn't restore, see `OnDeleted`), and the report +/// counts it so the photos don't go missing unexplained (#2589). A new virtual copy of it is +/// imported, not trashed with its master. +#[test] +fn a_catalog_photo_matching_a_trashed_record_stays_there_and_is_counted() { + let mut s = crate::Session::new(); + let trashed = library_photo(&mut s, "/missing/test.ARW", |p| p.deleted = true); + let r = apply(&mut s, sample(), false).unwrap(); + assert_eq!(r["mapping"]["1"], trashed.0, "{r}"); + assert!(s.catalog.photo(trashed).unwrap().deleted, "the photo stays in Recently Deleted"); + assert_eq!(r["inRecentlyDeleted"], 1, "{r}"); + let copy = s.catalog.photos().find(|p| p.copy_of == Some(trashed)).unwrap(); + assert!(!copy.deleted, "the new virtual copy is imported, not trashed"); + assert_eq!(r["imported"], 1, "only the copy was added: {r}"); +} #[test] fn preserves_existing_edits_and_rejects_invalid_structure_before_mutation() { let mut s = crate::Session::new(); diff --git a/crates/ui-egui/locales/cs-formats.json b/crates/ui-egui/locales/cs-formats.json index 1eb882f09..c60430c12 100644 --- a/crates/ui-egui/locales/cs-formats.json +++ b/crates/ui-egui/locales/cs-formats.json @@ -267,6 +267,7 @@ "Update “{name}” with Current Settings": "Aktualizovat „{name}“ podle aktuálního nastavení", "{n} preset{}": "Přednastavení: {n}{:.0}", "{count} more warnings.": "Další varování: {count}.", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "Fotografie katalogu v nedávno odstraněných této knihovny: {in_trash}. Zůstávají tam; chcete-li je vidět, obnovte je z nedávno odstraněných.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "Neimportované obrázky katalogu: {skipped}. Jejich záznamy v katalogu jsou neúplné.", "{photos} catalog records processed; {collections} collections.": "Zpracováno záznamů katalogu: {photos}; kolekcí: {collections}.", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "Chybějící originály: {missing}. Jejich záznamy jsou v katalogu dostupné; použijte Najít chybějící fotografie a znovu je propojte.", diff --git a/crates/ui-egui/locales/de-formats.json b/crates/ui-egui/locales/de-formats.json index 79390f136..9aa45bfb6 100644 --- a/crates/ui-egui/locales/de-formats.json +++ b/crates/ui-egui/locales/de-formats.json @@ -269,6 +269,7 @@ "{n} originals opened in the other editor": "{n} Originale im anderen Editor geöffnet", "· {in_source} identical copies": "· {in_source} identische Kopien", "{count} more warnings.": "{count} weitere Warnungen.", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "{in_trash} Katalogfotos liegen in dieser Bibliothek unter „Zuletzt gelöscht“ und bleiben dort; stelle sie dort wieder her, um sie zu sehen.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "{skipped} Katalogbilder wurden nicht importiert, weil ihre Einträge im Katalog unvollständig sind.", "{photos} catalog records processed; {collections} collections.": "{photos} Katalogeinträge verarbeitet; {collections} Sammlungen.", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "{missing} Originale fehlen. Ihre Katalogeinträge sind verfügbar; verwende „Fehlende Fotos suchen“, um sie neu zu verknüpfen.", diff --git a/crates/ui-egui/locales/es-formats.json b/crates/ui-egui/locales/es-formats.json index 33e5f52fc..a81dddb28 100644 --- a/crates/ui-egui/locales/es-formats.json +++ b/crates/ui-egui/locales/es-formats.json @@ -269,6 +269,7 @@ "{n} originals opened in the other editor": "{n} originales abiertos en el otro editor", "· {in_source} identical copies": "· {in_source} copias idénticas", "{count} more warnings.": "{count} advertencias más.", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "{in_trash} fotos del catálogo están en Eliminadas recientemente de esta biblioteca y permanecen allí; restáuralas desde Eliminadas recientemente para verlas.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "No se importaron {skipped} imágenes del catálogo porque sus registros en el catálogo están incompletos.", "{photos} catalog records processed; {collections} collections.": "{photos} registros del catálogo procesados; {collections} colecciones.", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "Faltan {missing} originales. Sus registros del catálogo están disponibles; usa Buscar fotos que faltan para volver a vincularlos.", diff --git a/crates/ui-egui/locales/fi-formats.json b/crates/ui-egui/locales/fi-formats.json index 1daab3dab..3babe2c75 100644 --- a/crates/ui-egui/locales/fi-formats.json +++ b/crates/ui-egui/locales/fi-formats.json @@ -257,6 +257,7 @@ "{pid} (missing)": "{pid} (puuttuu)", "{sel_n} selected · {counted}": "Valittu: {sel_n} · {counted}", "{size} GB": "{size} Gt", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "Katalogin kuvia tämän kirjaston Äskettäin poistetuissa: {in_trash}. Ne jäävät sinne; palauta ne Äskettäin poistetuista nähdäksesi ne.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "{skipped} catalog images were not imported because their records in the catalog are incomplete.", "{stage}… {:.0}%": "{stage}… {:.0}%", "{s} s of AV1 video": "{s} s AV1-videota", diff --git a/crates/ui-egui/locales/fr-formats.json b/crates/ui-egui/locales/fr-formats.json index 294273781..537dd7ed1 100644 --- a/crates/ui-egui/locales/fr-formats.json +++ b/crates/ui-egui/locales/fr-formats.json @@ -269,6 +269,7 @@ "{n} originals opened in the other editor": "{n} originaux ouverts dans l’autre éditeur", "· {in_source} identical copies": "· {in_source} copies identiques", "{count} more warnings.": "{count} avertissements supplémentaires.", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "{in_trash} photos du catalogue sont dans « Supprimés récemment » de cette photothèque et y restent ; restaurez-les depuis « Supprimés récemment » pour les voir.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "{skipped} images du catalogue n’ont pas été importées, car leurs entrées dans le catalogue sont incomplètes.", "{photos} catalog records processed; {collections} collections.": "{photos} entrées du catalogue traitées ; {collections} collections.", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "{missing} originaux sont manquants. Leurs entrées de catalogue sont disponibles ; utilisez Rechercher les photos manquantes pour rétablir les liens.", diff --git a/crates/ui-egui/locales/id-formats.json b/crates/ui-egui/locales/id-formats.json index 7fc325522..c596fa594 100644 --- a/crates/ui-egui/locales/id-formats.json +++ b/crates/ui-egui/locales/id-formats.json @@ -296,6 +296,7 @@ "“{to}” is already a person. Combining them makes one person; Undo separates them again.": "“{to}” sudah terdaftar sebagai orang. Jika digabungkan, keduanya menjadi satu orang; Urungkan akan memisahkannya lagi.", "{what} merge added at {mp}: its full size, {full}, needs more memory than this computer has": "Hasil penggabungan {what} ditambahkan dengan resolusi {mp}: resolusi penuhnya ({full}) memerlukan memori melebihi kapasitas komputer ini", "{what} merge added at {mp}, the largest a panorama can be (its full size is {full})": "Hasil penggabungan {what} ditambahkan dengan resolusi {mp}, resolusi maksimum untuk panorama (resolusi penuhnya {full})", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "{in_trash} foto katalog ada di Baru dihapus pada pustaka ini dan tetap di sana; pulihkan dari Baru dihapus untuk melihatnya.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "{skipped} catalog images were not imported because their records in the catalog are incomplete.", "Listing folders — folders: {folders}, files: {files}": "Listing folders — folders: {folders}, files: {files}", "Added {n} photo{}": "{n} foto ditambahkan{:.0}", diff --git a/crates/ui-egui/locales/it-formats.json b/crates/ui-egui/locales/it-formats.json index 7efae90e9..d9c6a5efe 100644 --- a/crates/ui-egui/locales/it-formats.json +++ b/crates/ui-egui/locales/it-formats.json @@ -250,6 +250,7 @@ "{s} s of AV1 video": "{s} s di video AV1", "Delete {n} effect preset{}? This can't be undone.": "Eliminare i preset effetto ({n})?{:.0} L'operazione non si può annullare.", "{count} more warnings.": "Altri avvisi: {count}.", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "{in_trash} foto del catalogo sono in Eliminate di recente di questa libreria e vi restano; ripristinale da Eliminate di recente per vederle.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "{skipped} immagini del catalogo non sono state importate perché i loro record nel catalogo sono incompleti.", "· {in_source} identical copies": "· copie identiche: {in_source}", "{what} merge added at {mp}, the largest a panorama can be (its full size is {full})": "Unione {what} aggiunta a {mp}, la dimensione massima di un panorama (la dimensione piena è {full})", diff --git a/crates/ui-egui/locales/ja-formats.json b/crates/ui-egui/locales/ja-formats.json index 99c14bf16..91cbbc995 100644 --- a/crates/ui-egui/locales/ja-formats.json +++ b/crates/ui-egui/locales/ja-formats.json @@ -273,6 +273,7 @@ "{n} originals opened in the other editor": "{n}個の元ファイルを別のエディタで開きました", "· {in_source} identical copies": "・同一のコピー {in_source} 件", "{count} more warnings.": "ほかに{count}件の警告があります。", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "カタログの写真 {in_trash} 枚は、このライブラリの「最近削除した写真」にあり、そのまま残ります。表示するには「最近削除した写真」から復元してください。", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "カタログ内のレコードが不完全なため、{skipped}件の画像を読み込めませんでした。", "{photos} catalog records processed; {collections} collections.": "カタログの{photos}件のレコードを処理しました。コレクションは{collections}件です。", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "{missing}件のオリジナルが見つかりません。カタログのレコードは利用できます。「見つからない写真を検索」で再リンクしてください。", diff --git a/crates/ui-egui/locales/ka-formats.json b/crates/ui-egui/locales/ka-formats.json index 460368261..c77de0407 100644 --- a/crates/ui-egui/locales/ka-formats.json +++ b/crates/ui-egui/locales/ka-formats.json @@ -279,6 +279,7 @@ "Updated {name}": "Updated {name}", "new, inside {parent}": "new, inside {parent}", "{count} more warnings.": "{count} more warnings.", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "კატალოგის ფოტოები ამ ბიბლიოთეკის ახლახან წაშლილებში: {in_trash}. ისინი იქ რჩება; სანახავად აღადგინეთ ისინი ახლახან წაშლილებიდან.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "კატალოგის {skipped} სურათი არ დაიმპორტდა, რადგან მათი ჩანაწერები კატალოგში არასრულია.", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.", "{n} JPEG files": "{n} JPEG files", diff --git a/crates/ui-egui/locales/pl-formats.json b/crates/ui-egui/locales/pl-formats.json index 20c7f840d..19fb9cdfe 100644 --- a/crates/ui-egui/locales/pl-formats.json +++ b/crates/ui-egui/locales/pl-formats.json @@ -261,6 +261,7 @@ "Update “{name}” with Current Settings": "Zaktualizuj „{name}” bieżącymi ustawieniami", "{n} preset{}": "Ustawienia: {n}{:.0}", "{count} more warnings.": "Pozostałe ostrzeżenia: {count}.", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "Zdjęcia katalogu w Ostatnio usuniętych tej biblioteki: {in_trash}. Pozostają tam; przywróć je z Ostatnio usuniętych, aby je zobaczyć.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "Nie zaimportowano obrazów z katalogu: {skipped}. Ich rekordy w katalogu są niekompletne.", "{photos} catalog records processed; {collections} collections.": "Przetworzone rekordy katalogu: {photos}; kolekcje: {collections}.", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "Brakujące oryginały: {missing}. Ich rekordy w katalogu są dostępne; użyj polecenia Znajdź brakujące zdjęcia, aby je ponownie powiązać.", diff --git a/crates/ui-egui/locales/pt-br-formats.json b/crates/ui-egui/locales/pt-br-formats.json index f17f2c0fd..7da70ee39 100644 --- a/crates/ui-egui/locales/pt-br-formats.json +++ b/crates/ui-egui/locales/pt-br-formats.json @@ -273,6 +273,7 @@ "{n} originals opened in the other editor": "{n} originais abertos no outro editor", "· {in_source} identical copies": "· {in_source} cópias idênticas", "{count} more warnings.": "Mais {count} avisos.", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "{in_trash} fotos do catálogo estão em Excluídos recentemente desta biblioteca e continuam lá; restaure-as de Excluídos recentemente para vê-las.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "{skipped} imagens do catálogo não foram importadas porque seus registros no catálogo estão incompletos.", "{photos} catalog records processed; {collections} collections.": "{photos} registros do catálogo processados; {collections} coleções.", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "{missing} originais estão ausentes. Seus registros no catálogo estão disponíveis; use Encontrar fotos ausentes para vinculá-los novamente.", diff --git a/crates/ui-egui/locales/ru-formats.json b/crates/ui-egui/locales/ru-formats.json index 76b7af427..f84b1f23c 100644 --- a/crates/ui-egui/locales/ru-formats.json +++ b/crates/ui-egui/locales/ru-formats.json @@ -269,6 +269,7 @@ "{n} originals opened in the other editor": "Открыто оригиналов в другом редакторе: {n}", "· {in_source} identical copies": "· идентичных копий: {in_source}", "{count} more warnings.": "Других предупреждений: {count}.", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "Фото каталога в «Недавно удалённых» этой библиотеки: {in_trash}. Они остаются там; чтобы увидеть их, восстановите их из «Недавно удалённых».", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "Не импортированы изображения каталога: {skipped}. Их записи в каталоге неполные.", "{photos} catalog records processed; {collections} collections.": "Обработано записей каталога: {photos}; коллекций: {collections}.", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "Не найдены оригиналы: {missing}. Их записи каталога доступны; используйте поиск отсутствующих фотографий, чтобы восстановить ссылки.", diff --git a/crates/ui-egui/locales/sv-formats.json b/crates/ui-egui/locales/sv-formats.json index 9a71a0288..6cd199d12 100644 --- a/crates/ui-egui/locales/sv-formats.json +++ b/crates/ui-egui/locales/sv-formats.json @@ -296,6 +296,7 @@ "Move {n} photo{} to “{}”?": "Move {n} photo{} to “{}”?", "Moved {n} photo{} to “{}”. Edit → Undo moves them back.": "Moved {n} photo{} to “{}”. Edit → Undo moves them back.", "{n} selected photos · {pages} pages": "{n} selected photos · {pages} pages", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "{in_trash} katalogfoton finns i Nyligen raderade i det här biblioteket och ligger kvar där; återställ dem från Nyligen raderade för att se dem.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "{skipped} catalog images were not imported because their records in the catalog are incomplete.", "Listing folders — folders: {folders}, files: {files}": "Listing folders — folders: {folders}, files: {files}", "Added {n} photo{}": "{n} foto tillagda{:.0}", diff --git a/crates/ui-egui/locales/uk-formats.json b/crates/ui-egui/locales/uk-formats.json index 36022ac1a..5f08d7a7d 100644 --- a/crates/ui-egui/locales/uk-formats.json +++ b/crates/ui-egui/locales/uk-formats.json @@ -269,6 +269,7 @@ "{n} originals opened in the other editor": "Відкрито оригіналів в іншому редакторі: {n}", "· {in_source} identical copies": "· ідентичних копій: {in_source}", "{count} more warnings.": "Інших попереджень: {count}.", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "Фото каталогу в нещодавно видалених цієї бібліотеки: {in_trash}. Вони залишаються там; щоб побачити їх, відновіть їх із нещодавно видалених.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "Не імпортовано зображення каталогу: {skipped}. Їхні записи в каталозі неповні.", "{photos} catalog records processed; {collections} collections.": "Оброблено записів каталогу: {photos}; колекцій: {collections}.", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "Не знайдено оригінали: {missing}. Їхні записи каталогу доступні; скористайтеся пошуком відсутніх фотографій, щоб відновити посилання.", diff --git a/crates/ui-egui/locales/vi-formats.json b/crates/ui-egui/locales/vi-formats.json index d5e28bff0..f4d482868 100644 --- a/crates/ui-egui/locales/vi-formats.json +++ b/crates/ui-egui/locales/vi-formats.json @@ -257,6 +257,7 @@ "{pid} (missing)": "{pid} (thiếu)", "{sel_n} selected · {counted}": "Đã chọn {sel_n} · {counted}", "{size} GB": "{size} GB", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "{in_trash} ảnh của danh mục nằm trong Đã xóa gần đây của thư viện này và vẫn ở đó; hãy khôi phục chúng từ Đã xóa gần đây để xem.", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "{skipped} catalog images were not imported because their records in the catalog are incomplete.", "{stage}… {:.0}%": "{stage}… {:.0}%", "{s} s of AV1 video": "{s} s of AV1 video", diff --git a/crates/ui-egui/locales/zh-hans-formats.json b/crates/ui-egui/locales/zh-hans-formats.json index 7406c39b8..9a06a534d 100644 --- a/crates/ui-egui/locales/zh-hans-formats.json +++ b/crates/ui-egui/locales/zh-hans-formats.json @@ -273,6 +273,7 @@ "{n} originals opened in the other editor": "已在其他编辑器中打开 {n} 个原始文件", "· {in_source} identical copies": "· {in_source} 个相同副本", "{count} more warnings.": "另有 {count} 条警告。", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "目录中有 {in_trash} 张照片位于此图库的“最近删除”中,并将保留在那里;如需查看,请从“最近删除”中恢复。", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "有 {skipped} 张图像未导入,因为它们在目录中的记录不完整。", "{photos} catalog records processed; {collections} collections.": "已处理 {photos} 条目录记录;{collections} 个收藏集。", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "有 {missing} 个原始文件缺失。其目录记录可用;请使用“查找缺失照片”重新链接文件。", diff --git a/crates/ui-egui/locales/zh-hant-formats.json b/crates/ui-egui/locales/zh-hant-formats.json index d5c4eb279..81b5d3c82 100644 --- a/crates/ui-egui/locales/zh-hant-formats.json +++ b/crates/ui-egui/locales/zh-hant-formats.json @@ -273,6 +273,7 @@ "{n} originals opened in the other editor": "已在其他編輯器中開啟 {n} 個原始檔案", "· {in_source} identical copies": "· {in_source} 個相同副本", "{count} more warnings.": "另有 {count} 則警告。", + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.": "目錄中有 {in_trash} 張照片位於此圖庫的「最近刪除」中,並會保留在那裡;如需檢視,請從「最近刪除」中復原。", "{skipped} catalog images were not imported because their records in the catalog are incomplete.": "有 {skipped} 張影像未匯入,因為它們在目錄中的記錄不完整。", "{photos} catalog records processed; {collections} collections.": "已處理 {photos} 筆目錄記錄;{collections} 個收藏集。", "{missing} originals are missing. Their catalog records are available; use Find Missing Photos to relink them.": "有 {missing} 個原始檔案遺失。其目錄記錄可用;請使用「尋找遺失相片」重新連結檔案。", diff --git a/crates/ui-egui/src/lightroom_import.rs b/crates/ui-egui/src/lightroom_import.rs index 098c989d5..83be31b7f 100644 --- a/crates/ui-egui/src/lightroom_import.rs +++ b/crates/ui-egui/src/lightroom_import.rs @@ -340,6 +340,13 @@ fn result_dialog(value: &Value, summary: &str) -> crate::state::Dialog { missing = missing )); } + let in_trash = report["inRecentlyDeleted"].as_u64().unwrap_or(0); + if in_trash > 0 { + details.push(crate::i18n::tr_format!( + "{in_trash} catalog photos are in this library's Recently Deleted and stay there; restore them from Recently Deleted to see them.", + in_trash = in_trash + )); + } // images whose catalog records are incomplete come as data (`skipped`), said here in the UI // language; their ids are in the report for the CLI and agents let skipped = report["skipped"].as_array().map_or(0, Vec::len); @@ -713,6 +720,18 @@ mod tests { assert_eq!(&en[2..], ["w1", "w2"]); } + #[test] + fn photos_left_in_recently_deleted_are_one_line() { + let value = json!({"report": {"photos": 3, "collections": 0, "inRecentlyDeleted": 2}}); + crate::i18n::set_language(crate::i18n::Locale::En); + let crate::state::Dialog::LightroomResult { details, .. } = result_dialog(&value, "done") else { panic!("not a Lightroom result") }; + assert_eq!(details.len(), 2, "{details:?}"); + assert!(details[1].starts_with("2 catalog photos are in this library's Recently Deleted"), "{details:?}"); + let value = json!({"report": {"photos": 3, "collections": 0, "inRecentlyDeleted": 0}}); + let crate::state::Dialog::LightroomResult { details, .. } = result_dialog(&value, "done") else { panic!("not a Lightroom result") }; + assert_eq!(details.len(), 1, "no line when none are: {details:?}"); + } + #[test] fn skipped_images_do_not_take_the_warning_slots() { let warnings: Vec<_> = (0..10).map(|i| format!("w{i}")).collect();