mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-24 10:09:01 -06:00
Merge PR #2041: fix(camera-profiles): invalidate cached renders after subsecond profile changes
This commit is contained in:
@@ -159,29 +159,31 @@ pub fn get(model: &str) -> Option<Arc<CameraProfile>> {
|
||||
/// keys, so thumbnails rendered before a profile existed are not reused after.
|
||||
pub fn cache_key() -> u64 {
|
||||
static KEY: OnceLock<u64> = OnceLock::new();
|
||||
*KEY.get_or_init(|| {
|
||||
let entries = dir().and_then(|d| std::fs::read_dir(d).ok());
|
||||
let mut files: Vec<(String, u64, u64)> = entries
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flatten()
|
||||
.filter(|e| e.path().extension().is_some_and(|x| x == "json"))
|
||||
.filter_map(|e| {
|
||||
let meta = e.metadata().ok()?;
|
||||
let modified = meta.modified().ok()?.duration_since(std::time::UNIX_EPOCH).ok()?.as_secs();
|
||||
Some((e.file_name().to_string_lossy().into_owned(), meta.len(), modified))
|
||||
})
|
||||
.collect();
|
||||
files.sort();
|
||||
let mut h = lightcraft_preview::Hasher128::new();
|
||||
for (name, len, modified) in &files {
|
||||
h.str(name).u64(*len).u64(*modified);
|
||||
}
|
||||
for (model, json) in BUNDLED {
|
||||
h.str(model).str(json);
|
||||
}
|
||||
h.finish().0 as u64
|
||||
})
|
||||
*KEY.get_or_init(|| cache_key_for(dir().as_deref()))
|
||||
}
|
||||
|
||||
fn cache_key_for(directory: Option<&Path>) -> u64 {
|
||||
let entries = directory.and_then(|d| std::fs::read_dir(d).ok());
|
||||
let mut files: Vec<(String, u64, std::time::Duration)> = entries
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flatten()
|
||||
.filter(|e| e.path().extension().is_some_and(|x| x == "json"))
|
||||
.filter_map(|e| {
|
||||
let meta = e.metadata().ok()?;
|
||||
let modified = meta.modified().ok()?.duration_since(std::time::UNIX_EPOCH).ok()?;
|
||||
Some((e.file_name().to_string_lossy().into_owned(), meta.len(), modified))
|
||||
})
|
||||
.collect();
|
||||
files.sort();
|
||||
let mut h = lightcraft_preview::Hasher128::new();
|
||||
for (name, len, modified) in &files {
|
||||
h.str(name).u64(*len).u64(modified.as_secs()).u64(u64::from(modified.subsec_nanos()));
|
||||
}
|
||||
for (model, json) in BUNDLED {
|
||||
h.str(model).str(json);
|
||||
}
|
||||
h.finish().0 as u64
|
||||
}
|
||||
|
||||
/// Per camera model: the photos read and their pooled colour pairs.
|
||||
@@ -294,6 +296,38 @@ mod tests {
|
||||
assert!(bundled("No Such Camera").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_second_profile_replacement_changes_render_cache_key() {
|
||||
let dir = std::env::temp_dir().join(format!("lc-camera-profile-cache-{}", std::process::id()));
|
||||
let mut first = profile();
|
||||
first.model = "ILCE-7CR".into();
|
||||
let path = save(&first, &dir).unwrap();
|
||||
let set_modified = |millis| {
|
||||
let time = std::time::UNIX_EPOCH + std::time::Duration::from_secs(1_700_000_000) + std::time::Duration::from_millis(millis);
|
||||
let file = std::fs::OpenOptions::new().write(true).open(&path).unwrap();
|
||||
file.set_times(std::fs::FileTimes::new().set_modified(time)).unwrap();
|
||||
file.metadata().unwrap()
|
||||
};
|
||||
let before = set_modified(100);
|
||||
assert_eq!(load(&path).unwrap(), first);
|
||||
let before_key = cache_key_for(Some(&dir));
|
||||
|
||||
let mut second = first;
|
||||
second.matrix[0][0] = 1.7;
|
||||
save(&second, &dir).unwrap();
|
||||
let after = set_modified(350);
|
||||
assert_eq!(load(&path).unwrap(), second);
|
||||
assert_eq!(before.len(), after.len());
|
||||
let before_time = before.modified().unwrap().duration_since(std::time::UNIX_EPOCH).unwrap();
|
||||
let after_time = after.modified().unwrap().duration_since(std::time::UNIX_EPOCH).unwrap();
|
||||
assert_eq!(before_time.as_secs(), after_time.as_secs());
|
||||
assert_ne!(before_time.subsec_nanos(), after_time.subsec_nanos());
|
||||
let after_key = cache_key_for(Some(&dir));
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
|
||||
assert_ne!(before_key, after_key, "a changed profile must not reuse the previous render cache");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_names_are_safe() {
|
||||
assert_eq!(file_name("ILCE-7M4"), "ILCE-7M4.json");
|
||||
|
||||
Reference in New Issue
Block a user