mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-24 10:09:01 -06:00
Sidebar: count missing files on a worker thread; read browsed folders in smaller batches
The Missing count stat()ed every catalog photo on the UI thread, which on a network share (and with browsed NAS photos in the catalog) froze the window for seconds during a folder browse. It now runs in the background and shows the last finished count. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
919e65efaf
commit
e076f65df8
@@ -291,7 +291,8 @@ pub fn start(app: &mut LightcraftApp, d: &ImportDialog) -> Result<Value, String>
|
||||
/// Run one batch of the import in progress (called every frame).
|
||||
pub fn tick(app: &mut LightcraftApp, ctx: &egui::Context) {
|
||||
let Some(task) = app.import.as_mut() else { return };
|
||||
let n = task.queue.len().min(BATCH);
|
||||
// (browsing reads a sidecar per file, slow on a network share: keep each frame short)
|
||||
let n = task.queue.len().min(if task.browse { 2 } else { BATCH });
|
||||
let batch: Vec<String> = task.queue.drain(..n).collect();
|
||||
let mut p = task.params.clone();
|
||||
p["paths"] = json!(batch);
|
||||
|
||||
@@ -146,20 +146,51 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
||||
});
|
||||
}
|
||||
|
||||
/// How many library photos have no file (cached in egui memory, refreshed every 5 s).
|
||||
/// How many library photos have no file. Checking stats every file, which on a network share
|
||||
/// takes seconds, so it runs on a worker thread: the count shown is the last finished one,
|
||||
/// refreshed at most every 5 s (or when the catalog changed).
|
||||
fn missing_count(app: &mut LightcraftApp, ui: &mut egui::Ui) -> usize {
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed};
|
||||
#[derive(Clone, Default)]
|
||||
struct Job {
|
||||
n: std::sync::Arc<AtomicUsize>,
|
||||
running: std::sync::Arc<AtomicBool>,
|
||||
at: f64,
|
||||
rev: u64,
|
||||
}
|
||||
let id = egui::Id::new("missing-count");
|
||||
let now = ui.input(|i| i.time);
|
||||
let rev = app.session.catalog.revision;
|
||||
if let Some((n, at, r)) = ui.data(|d| d.get_temp::<(usize, f64, u64)>(id))
|
||||
&& now - at < 5.0
|
||||
&& r == rev
|
||||
{
|
||||
return n;
|
||||
let mut job: Job = ui.data(|d| d.get_temp(id)).unwrap_or(Job { rev: u64::MAX, at: f64::MIN, ..Default::default() });
|
||||
let fresh = now - job.at < 5.0 && job.rev == rev;
|
||||
if !fresh && !job.running.load(Relaxed) {
|
||||
let paths: Vec<String> = app
|
||||
.session
|
||||
.catalog
|
||||
.photos()
|
||||
.filter(|p| !p.deleted)
|
||||
.filter_map(|p| match &p.source {
|
||||
lightcraft_catalog::Source::File { path } => Some(path.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
job.running.store(true, Relaxed);
|
||||
job.at = now;
|
||||
job.rev = rev;
|
||||
let (n, running, ctx) = (job.n.clone(), job.running.clone(), ui.ctx().clone());
|
||||
let work = move || {
|
||||
let missing = if cfg!(target_arch = "wasm32") { 0 } else { paths.iter().filter(|p| !std::path::Path::new(p).exists()).count() };
|
||||
n.store(missing, Relaxed);
|
||||
running.store(false, Relaxed);
|
||||
ctx.request_repaint();
|
||||
};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
std::thread::spawn(work);
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
work();
|
||||
ui.data_mut(|d| d.insert_temp(id, job.clone()));
|
||||
}
|
||||
let n = lightcraft_engine::cmd::missing::missing(&app.session).len();
|
||||
ui.data_mut(|d| d.insert_temp(id, (n, now, rev)));
|
||||
n
|
||||
job.n.load(Relaxed)
|
||||
}
|
||||
|
||||
/// Folders on this computer to browse without adding (Lightroom's Local): Pictures, Desktop,
|
||||
|
||||
Reference in New Issue
Block a user