mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-01 05:57:09 -06:00
LR-IMP-ADD-DIALOG: an import's probe opens each file once
The probe asked for a file's size, read it (an open and its own size question), and then asked for its modification time again by path: three round trips to a network share's server for what one open can answer. It now opens the file once and takes the size and the modification time from the open file. What is read, the work-gate weight, and the text of the error for a file that can't be read are unchanged.
This commit is contained in:
@@ -76,11 +76,17 @@ pub fn fs_pair_loader() -> PairLoader {
|
||||
pub fn fs_hooks() -> (FileLoader, FileProbe) {
|
||||
let loader: FileLoader = Arc::new(|path: &str, max_edge: usize| fs_load(path, max_edge, |bytes| load_vec(bytes, max_edge)));
|
||||
let probe: FileProbe = Arc::new(|path: &str| {
|
||||
let len = std::fs::metadata(path).map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX)).unwrap_or(0);
|
||||
use std::io::Read;
|
||||
// one open, then everything from the open file: its size and modification time are not
|
||||
// round trips of their own (on a network share each of them is one)
|
||||
let mut file = std::fs::File::open(path).map_err(|e| format!("{path}: {e}"))?;
|
||||
let meta = file.metadata().ok();
|
||||
let len = meta.as_ref().map_or(0, |m| usize::try_from(m.len()).unwrap_or(usize::MAX));
|
||||
let _permit = crate::memory::work_gate().acquire(len);
|
||||
let bytes = std::fs::read(path).map_err(|e| format!("{path}: {e}"))?;
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes).map_err(|e| format!("{path}: {e}"))?;
|
||||
let mut info = probe_bytes(path, &bytes)?;
|
||||
info.file_modified = file_modified(path);
|
||||
info.file_modified = meta.as_ref().and_then(modified_iso);
|
||||
Ok(info)
|
||||
});
|
||||
(loader, probe)
|
||||
@@ -104,10 +110,10 @@ fn fs_load<T>(path: &str, max_edge: usize, load: impl FnOnce(Vec<u8>) -> Result<
|
||||
r
|
||||
}
|
||||
|
||||
/// `path`'s modification time as ISO 8601 (see [`ProbeInfo::file_modified`]); `None` when the
|
||||
/// The modification time in `meta` as ISO 8601 (see [`ProbeInfo::file_modified`]); `None` when the
|
||||
/// filesystem doesn't say or the time is before 1970.
|
||||
fn file_modified(path: &str) -> Option<String> {
|
||||
let t = std::fs::metadata(path).ok()?.modified().ok()?;
|
||||
fn modified_iso(meta: &std::fs::Metadata) -> Option<String> {
|
||||
let t = meta.modified().ok()?;
|
||||
let secs = i64::try_from(t.duration_since(std::time::UNIX_EPOCH).ok()?.as_secs()).ok()?;
|
||||
Some(crate::import::civil(secs))
|
||||
}
|
||||
|
||||
@@ -427,6 +427,21 @@ fn undated_files_take_the_file_date() {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// The probe opens the file once and says the same thing as before about one that can't be read:
|
||||
/// its path, then the system's words (a missing file, a folder).
|
||||
#[test]
|
||||
fn a_probe_of_a_file_that_cannot_be_read_names_the_path_and_the_cause() {
|
||||
let dir = std::env::temp_dir().join(format!("lightcraft-probe-errors-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let (_, probe) = fs_hooks();
|
||||
for path in [dir.join("no-such-file.jpg"), dir.clone()] {
|
||||
let path = path.to_string_lossy().to_string();
|
||||
let e = probe(&path).unwrap_err();
|
||||
assert_eq!(e, format!("{path}: {}", std::fs::read(&path).unwrap_err()));
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// Issue #367: probes read headers, not pixels, and report what the decode-based probe did:
|
||||
/// oriented dimensions (EXIF orientation 6 swaps them), metadata, the error for a truncated
|
||||
/// file or one that isn't an image.
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# What's new in LightCraft
|
||||
|
||||
## 2026-10-27
|
||||
|
||||
### Reading a photo from a network share asks the server less
|
||||
- Checking a photo (before an import, and for Reload, Recheck, Relink and Convert) asked for the file's size, then
|
||||
opened it, then asked for its modification time as a question of its own. It now opens the file once and reads
|
||||
both from the open file, so each photo costs the server fewer round trips (about a quarter less time to read a
|
||||
set of photos from an SMB share, in one measurement on one share). What is imported and the messages for a file
|
||||
that can't be read are the same.
|
||||
|
||||
## 2026-10-24
|
||||
|
||||
### Relative-date filters and smart albums follow the clock
|
||||
|
||||
Reference in New Issue
Block a user