diff --git a/crates/engine/src/lightroom_catalog.rs b/crates/engine/src/lightroom_catalog.rs index 3066187ce..03190dd8f 100644 --- a/crates/engine/src/lightroom_catalog.rs +++ b/crates/engine/src/lightroom_catalog.rs @@ -1016,6 +1016,10 @@ pub(crate) fn apply_prepared(s: &mut crate::Session, mut data: CatalogImport, co } p }; + // Offline videos bypass file probing; Lightroom's format still identifies their kind. + if text(&src.image, "fileFormat").eq_ignore_ascii_case("VIDEO") { + p.kind = lightcraft_catalog::MediaKind::Video; + } ids.insert(src.source_id, p.id); index.photos.insert(identity(&data, src), p.id.0); let keep_develop = existing_id.is_some() && p.edited.is_some() && !update_existing; @@ -1080,7 +1084,14 @@ pub(crate) fn apply_prepared(s: &mut crate::Session, mut data: CatalogImport, co if copy_masters.contains(&src.source_id) { planned.insert(photo_id, p.clone()); } - if s.catalog.photo(p.id).is_some() { + if let Some(old) = s.catalog.photo(p.id) { + if old.kind != p.kind { + // Repair older imports with existing reversible operations. Removing just the + // record leaves album/stack membership intact; the enclosing batch is atomic. + let mut repaired = (**old).clone(); + repaired.kind = p.kind; + ops.extend([Op::RemovePhoto { id: p.id }, Op::AddPhoto { photo: Box::new(repaired) }]); + } if was_local && promoted.insert(p.id) { imported += 1; ops.push(Op::SetLocal { id: p.id, local: false }); diff --git a/crates/engine/src/lightroom_catalog/tests.rs b/crates/engine/src/lightroom_catalog/tests.rs index 654b40458..7e93131ce 100644 --- a/crates/engine/src/lightroom_catalog/tests.rs +++ b/crates/engine/src/lightroom_catalog/tests.rs @@ -548,6 +548,83 @@ fn photo_tables(root: (&str, &str)) -> Vec<(&'static str, &'static str, Vec`) or “This photo can't be opened” (`compare:renderFailed:`) as the loupe does. Escape → Detail without losing edits; pins are catalog-local; missing/deleted photos refused and deleted references no longer drawn (Windows regression/native smoke verified) | | LRC-LIB-CATALOG-PANEL | Catalog sets | P1 | ✅ | `cmd:library.source` (`missing`) | all, recently added, picks, recently deleted, missing photos (when any) | -| LRC-LIB-CATALOG-IMPORT | Native Lightroom Classic catalog import | P1 | 🟡 | `cmd:library.inspectLightroom`, `cmd:library.importLightroom`, `cmd:file.importLightroom`, `crates/engine/src/lightroom_catalog.rs`, `crates/ui-egui/src/dialogs/lightroom_destination.rs`, `crates/ui-egui/src/tests/lightroom_destination.rs`, `docs/lightroom-catalog-import.md` | before the import starts, File ▸ Import Lightroom Catalog… asks where the catalog goes: merge into the open library (named, with its photo count; preselected when it is empty) or create a new library (named after the catalog file, next to the open library, both editable), which is made and opened following File ▸ New Library…'s rules before the import; Cancel does nothing; Settings ▸ General ▸ "When importing a Lightroom catalog" (Ask where to import it / Always merge into the open library) or the window's "Don't ask again" turns it off; `file.importLightroom` takes `into` (`open` / `new`), `name`, `location` to choose without the window; a session in memory merges without asking (issue #2672); read-only `.lrcat` plus committed WAL pages (also WAL-only tables with main schema format 0), in-tree read-only Rust SQLite B-tree reader; on-demand main-file pages up to 16 GiB with existing table/record bounds (WAL still capped at 1 GiB, issue #798); background inspection/import with progress/cancellation, per-open library identity checks; persistent result with destination library, offline-original counts and Show in Library, dismissed when switching libraries; offline Local records are promoted without probing missing files; originals in place, ratings/flags/labels, XMP, hierarchical keywords, collection sets/regular collections, virtual copies, mapped develop settings; stable reimport identities, undo, missing-file placeholders, a root folder not found at its recorded path looked up relative to the catalog (Lightroom's `relativePathFromCatalog`, so a project folder copied from another computer stays linked; reported as a warning), existing edits preserved. Best-effort deduplicated recovery archives retain settings/history/snapshots/collection rules (32 MiB each, eight archives/128 MiB aggregate); archive limits/write failures and optional-table limits warn without blocking photo import; unrelated unsupported tables and opaque SQL BLOBs skipped. Custom white balance on relative-WB raws (`RAW` in Lightroom's terms) stays As Shot and is reported, since the catalog keeps no as-shot white to shift from (issue #510). An image whose file, folder or root folder record is missing from the catalog, or whose original path is empty, is skipped with its virtual copies instead of failing the whole import; the report lists them as data (`skipped`: image id and reason) and the result window counts them in the UI language (PR #2230). Adobe rendering, AI data and native history replay remain incomplete; no writes to Lightroom catalogs. Open Library given a Lightroom catalog folder, an `.lrdata` / `.lrcat-data` folder or the `.lrcat` makes no library there and offers this import with the catalog found (`cmd:app.openLibrary`, `crates/engine/src/lightroom_folder.rs`; issue #891) | +| LRC-LIB-CATALOG-IMPORT | Native Lightroom Classic catalog import | P1 | 🟡 | `cmd:library.inspectLightroom`, `cmd:library.importLightroom`, `cmd:file.importLightroom`, `crates/engine/src/lightroom_catalog.rs`, `crates/ui-egui/src/dialogs/lightroom_destination.rs`, `crates/ui-egui/src/tests/lightroom_destination.rs`, `docs/lightroom-catalog-import.md` | before the import starts, File ▸ Import Lightroom Catalog… asks where the catalog goes: merge into the open library (named, with its photo count; preselected when it is empty) or create a new library (named after the catalog file, next to the open library, both editable), which is made and opened following File ▸ New Library…'s rules before the import; Cancel does nothing; Settings ▸ General ▸ "When importing a Lightroom catalog" (Ask where to import it / Always merge into the open library) or the window's "Don't ask again" turns it off; `file.importLightroom` takes `into` (`open` / `new`), `name`, `location` to choose without the window; a session in memory merges without asking (issue #2672); read-only `.lrcat` plus committed WAL pages (also WAL-only tables with main schema format 0), in-tree read-only Rust SQLite B-tree reader; on-demand main-file pages up to 16 GiB with existing table/record bounds (WAL still capped at 1 GiB, issue #798); background inspection/import with progress/cancellation, per-open library identity checks; persistent result with destination library, offline-original counts and Show in Library, dismissed when switching libraries; offline Local records are promoted without probing missing files; originals in place, ratings/flags/labels, XMP, hierarchical keywords, collection sets/regular collections, virtual copies, mapped develop settings; stable reimport identities, undo, missing-file placeholders; VIDEO records match the Videos filter, with reimport repairing older image classification while keeping edits and memberships; a root folder not found at its recorded path looked up relative to the catalog (Lightroom's `relativePathFromCatalog`, so a project folder copied from another computer stays linked; reported as a warning), existing edits preserved. Best-effort deduplicated recovery archives retain settings/history/snapshots/collection rules (32 MiB each, eight archives/128 MiB aggregate); archive limits/write failures and optional-table limits warn without blocking photo import; unrelated unsupported tables and opaque SQL BLOBs skipped. Custom white balance on relative-WB raws (`RAW` in Lightroom's terms) stays As Shot and is reported, since the catalog keeps no as-shot white to shift from (issue #510). An image whose file, folder or root folder record is missing from the catalog, or whose original path is empty, is skipped with its virtual copies instead of failing the whole import; the report lists them as data (`skipped`: image id and reason) and the result window counts them in the UI language (PR #2230). Adobe rendering, AI data and native history replay remain incomplete; no writes to Lightroom catalogs. Open Library given a Lightroom catalog folder, an `.lrdata` / `.lrcat-data` folder or the `.lrcat` makes no library there and offers this import with the catalog found (`cmd:app.openLibrary`, `crates/engine/src/lightroom_folder.rs`; issue #891) | | LRC-LIB-CATALOG-LOCK | Catalog open in one program at a time | P1 | ✅ | `cmd:app.openLibrary`, `crates/catalog/src/lock.rs`, `crates/engine/src/library.rs` (`open_library`) | exclusive OS lock on `catalog.lock` for the session (app and `lightcraft-cli --library`); a second opener is refused with who has it (`catalog.lock.owner`: program, process, computer); released by the OS on a crash, so never stale; file systems without locks open unprotected with a warning (the app's notices, `library.info` → `settingsWarnings`, and the CLI's stderr; issue #171); no read-only mode; the browser build (OPFS) has no guard across tabs yet. A library that can't be opened at launch (locked, unreadable, newer format, drive missing) shows a window — Try Again, Choose Another Library…, Continue Without Saving (then a banner; nothing written), Quit — instead of a silent demo session (`crates/ui-egui/src/panels/library_problem.rs`); the lock is released explicitly when the library closes, not by letting its file handle close, so a program another thread is starting at that moment (which holds a copy of every file handle until it is launched) cannot keep the library "already open" for a moment (issue #680) | | LRC-LIB-CATALOG-SWITCH | New / Open Recent catalog, catalog name shown | P1 | ✅ | `cmd:app.newLibrary`, `cmd:app.openLibrary`, `cmd:app.recentLibraries`, `cmd:app.clearRecentLibraries`, `crates/ui-egui/src/dialogs/settings.rs`, `crates/ui-egui/src/panels/left.rs`, `crates/ui-egui/src/panels/library_problem.rs` (`launch_plan`, `show_chooser`), `crates/ui-egui/src/tests/launch_library.rs` | File ▸ New Library… (name, then location), File ▸ Open Recent Library (last 8; missing ones greyed out, dropped when opened; Remove from List ▸ one entry, or `app.recentLibraries {remove}`, which forgets only the path; Clear), the open library's name under My Photos and in the window title (issue #902); Open/New refused during imports and their folder scans; a library that can't be opened leaves the current one and its reference pin intact; a successful switch clears the catalog-local reference pin; ⌘O (Ctrl+O on Windows and Linux) runs Open Library… and asks for a folder once (`crates/ui-egui/src/tests/keymap.rs`: `cmd_o_opens_the_library_picker_once`); a dialog or a text field with the focus keeps the key; Settings ▸ General ▸ At launch: Open the last library (default) or Ask me, a window listing the recent libraries (name, folder, when last opened; missing, unreadable or no-longer-library ones greyed out with the reason, checked read-only — nothing is locked or written, so a library another program has open is reported, with who has it, when Open is pressed; the last one selected; Open, Open Another…, New Library…, Remove from List, Quit; ↑ ↓ Return Esc), also shown once when Alt/Option is held in the first 2 s after the window appears (macOS reports an Option key held since before launch only once it changes); `--library` and `LIGHTCRAFT_LIBRARY` come first and `lightcraft-cli` never asks (issue #1290) | | LRC-LIB-FOLDERS | Disk folder tree | P1 | ✅ | `cmd:library.browse`, `cmd:folder.rename`, `cmd:folder.move`, `crates/ui-egui/src/panels/left.rs` (`local_section`, `folder_tree`) | standard places + the browsed folder + Browse Folder…, each an expandable tree of subfolders, breadcrumb navigation; context menu: Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked; one undo step each: undo / redo move the folder back and forth, refused when the destination is taken) | @@ -846,6 +846,7 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove | 2026-10-13 | minor | LRC-LIB-KEYWORDS: the Keyword List marks a keyword that has an applied keyword inside it (a dot in its box); status unchanged | | 2026-10-12 | minor | LR-IMP-FORMATS: folder discovery includes SR2/SRF; MCP and CLI reuse the engine extension list. DSC-F828 remains preview-only; decoder support and row status unchanged | | 2026-10-15 | patch | KEYC-RATING: `\` shows / hides the Filter Bar in Photo Grid and Square Grid (Show Original in Detail); status unchanged (🟡) | +| 2026-10-16 | patch | LRC-LIB-CATALOG-IMPORT: VIDEO records appear in the Videos filter; reimport repairs older classification with edits, memberships and undo preserved (#1940, filter issue only). Status and summary counts unchanged. | | 2026-10-12 | minor | LR-EDIT-OPTICS-PROFILE: Sony ARW shading, CA and distortion tables decoded with settings and provenance (raw IFD, `SR2SubIFD` fallback); applied corrections unchanged (ILCE-7RM4A and ILCE-7RM6 distortion); status unchanged (🟡) | | 2026-10-14 | minor | LR-IMP-CAMERA-COVERAGE: Camera-coverage sweep (#1327): 30 models verified on 40 CC0 files (coverage.py 515 → 545 verified); sensor verification does not establish colour parity (#1277); status unchanged (🟡) | | 2026-10-13 | minor | LR-MERGE-PANO: a panorama's DNG takes its `AsShotNeutral` from the median of its frames' vendor white-balance multipliers (CR2, NEF, ARW…) instead of the first frame's tags, and frames now carry the multipliers (issue #1144); status unchanged (✅) |