From 881bc85dacd7feb3168d7134c21db520453b4698 Mon Sep 17 00:00:00 2001 From: radialmonster Date: Thu, 8 Oct 2026 15:23:57 -0400 Subject: [PATCH] LR-IMP-FORMATS: import CRW, MRW, X3F, KDC, MOS, ERF and IIQ files from folders as preview only Folder import, drag and drop and MCP folder expansion skipped these extensions, so the preview-only fallback for these containers was only reachable by naming a file. Adds exactly the containers that now import as preview only to import::EXTENSIONS and the headless PHOTO_EXTENSIONS. 3FR, FFF, DCR and STI still fail and stay out. --- crates/engine/src/import.rs | 3 ++- crates/engine/src/tests_import.rs | 42 +++++++++++++++++++++++++++++++ crates/mcp/src/headless.rs | 3 ++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/import.rs b/crates/engine/src/import.rs index 5df94988..778579ea 100644 --- a/crates/engine/src/import.rs +++ b/crates/engine/src/import.rs @@ -33,7 +33,8 @@ use crate::media::ProbeInfo; /// File extensions LightCraft imports (lower case). pub const EXTENSIONS: &[&str] = &[ "jpg", "jpeg", "png", "tif", "tiff", "webp", "dng", "cr2", "cr3", "nef", "nrw", "arw", "raf", "orf", "rw2", "rwl", "raw", "pef", "psd", "jxl", - "gif", "bmp", "heic", "avif", + "gif", "bmp", "heic", "avif", // containers LightCraft cannot decode but imports as preview only (their embedded JPEG) + "iiq", "crw", "mrw", "x3f", "kdc", "mos", "erf", ]; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] diff --git a/crates/engine/src/tests_import.rs b/crates/engine/src/tests_import.rs index ce36c447..1410fde0 100644 --- a/crates/engine/src/tests_import.rs +++ b/crates/engine/src/tests_import.rs @@ -1007,3 +1007,45 @@ fn trashed_photo_ids(tag: &str) -> (Session, std::path::PathBuf, [u64; 1], u64) let (s, src, id) = trashed_photo(tag); (s, src, [id], 0) } +/// Raw containers LightCraft cannot decode but shows by their embedded JPEG are picked up from a folder +/// (the extensions are in `import::EXTENSIONS`; the files differ so none is a duplicate) and import as preview only. +#[test] +fn folder_import_picks_up_undecodable_raw_containers_as_preview_only() { + use lightcraft_codecs::{ChromaSubsampling, EncodeImage, EncodeMeta, Samples, encode_jpeg}; + let src = temp_dir("preview-only-exts"); + let px: Vec = (0..40 * 30).flat_map(|i| [(i % 251) as u8, 128, 200]).collect(); + let jpeg = encode_jpeg(&EncodeImage::new(40, 30, 3, Samples::U8(&px)), 90, ChromaSubsampling::S444, &EncodeMeta::default()).unwrap(); + // CRW / MRW / X3F: their magic, then the JPEG. IIQ / KDC / MOS / ERF: a TIFF whose IFD0 only points at the JPEG. + let tiff = { + let off = 8 + 2 + 24 + 4; + let mut f = b"II*\0\x08\0\0\0\x02\0".to_vec(); + for (tag, v) in [(513u16, off as u32), (514, jpeg.len() as u32)] { + f.extend_from_slice(&tag.to_le_bytes()); + f.extend_from_slice(&[4, 0, 1, 0, 0, 0]); + f.extend_from_slice(&v.to_le_bytes()); + } + f.extend_from_slice(&[0, 0, 0, 0]); + f.extend_from_slice(&jpeg); + f + }; + let with_magic = |magic: &[u8]| [magic, &[0x11u8; 64][..], &jpeg].concat(); + let files: [(&str, Vec); 7] = [ + ("a.CRW", with_magic(b"II\x1a\0\0\0HEAPCCDR")), + ("b.mrw", with_magic(b"\0MRM\0\x01\0\0")), + ("c.x3f", with_magic(b"FOVb\x02\0\x02\0")), + ("d.iiq", [&tiff[..], b"d"].concat()), + ("e.kdc", [&tiff[..], b"e"].concat()), + ("f.mos", [&tiff[..], b"f"].concat()), + ("g.erf", [&tiff[..], b"g"].concat()), + ]; + for (name, bytes) in &files { + std::fs::write(src.join(name), bytes).unwrap(); + } + let mut s = Session::new().with_fs(); + let r = s.execute("library.import", &json!({"paths": [src.to_string_lossy()]})).unwrap(); + assert_eq!(r["scanned"], 7, "{r}"); + assert_eq!(ids(&r, "imported"), 7, "{r}"); + assert_eq!(ids(&r, "failed"), 0, "{r}"); + assert!(s.catalog.photos().all(|p| p.preview_only.is_some() && p.kind == lightcraft_catalog::MediaKind::Raw && (p.width, p.height) == (40, 30))); + let _ = std::fs::remove_dir_all(&src); +} diff --git a/crates/mcp/src/headless.rs b/crates/mcp/src/headless.rs index bfc5ea1e..7e18f738 100644 --- a/crates/mcp/src/headless.rs +++ b/crates/mcp/src/headless.rs @@ -13,7 +13,8 @@ use crate::backend::Backend; /// File extensions recognised as photos when expanding folders. pub const PHOTO_EXTENSIONS: &[&str] = &[ "jpg", "jpeg", "png", "tif", "tiff", "webp", "dng", "cr2", "cr3", "nef", "arw", "raf", "orf", "rw2", "rwl", "raw", "pef", "psd", "jxl", "gif", - "bmp", "avif", + "bmp", "avif", // containers LightCraft cannot decode but imports as preview only (their embedded JPEG) + "iiq", "crw", "mrw", "x3f", "kdc", "mos", "erf", ]; /// Headless backend: a [`Session`] with filesystem hooks.