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.
This commit is contained in:
radialmonster 2026-10-08 15:23:57 -04:00
parent dbe73f0f9c
commit 881bc85dac
3 changed files with 46 additions and 2 deletions

View File

@ -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)]

View File

@ -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<u8> = (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<u8>); 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);
}

View File

@ -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.