diff --git a/ui/dr-ui/src/faces.rs b/ui/dr-ui/src/faces.rs index a323b3a..6efc3a6 100644 --- a/ui/dr-ui/src/faces.rs +++ b/ui/dr-ui/src/faces.rs @@ -33,7 +33,14 @@ use dr_thumbs::{ThumbSize, ThumbStore}; use dr_types::settings::FaceSettings; use dr_types::ImageId; -/// The tier faces are found on. See the module note. +/// The tier a stored face crop is cut from. See the module note. +/// +/// **No longer the tier faces are *found* on**, and the distinction is the +/// whole of `docs/faces.md` §7c: 1024 is below [`dr_face::MIN_DETECT_EDGE`], so +/// detection refuses it. Cutting a crop out of a proxy this size is fine — the +/// face has already been located, and the crop only has to be looked at. +/// Finding one at this size is what returned 0.078 faces per image where the +/// same photographs returned 1.82. pub const FACE_TIER: ThumbSize = ThumbSize::Large; /// Progress from an indexing sweep. @@ -120,11 +127,15 @@ pub fn faces_outstanding( #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct IndexAudit { pub coverage: faces::Coverage, - /// Outstanding and ready: the proxy exists, so a sweep would do these now. + /// Outstanding, with a proxy already on disk. + /// + /// **Not "ready to index".** It was, when a pass existed that detected on + /// the stored proxy; that proxy is 1024 and detection now refuses it + /// (§7c), so these need a fetch exactly like the others do. Kept as a + /// separate number only because it says something true about the cache. pub ready: u64, - /// Outstanding with no proxy on disk, so the whole-library pass will fetch - /// one. Still reported separately because it is the expensive half — these - /// cost a range request each, and the ones above cost nothing. + /// Outstanding with no proxy on disk. Costs a range request, same as the + /// ones above now do. pub awaiting_proxy: u64, } @@ -151,13 +162,14 @@ impl IndexAudit { "{}/{} images indexed ({shown}), {} face(s), {} image(s) with none", c.indexed, c.images, c.faces, c.without_faces, ); - if self.ready > 0 { - s.push_str(&format!("; {} ready to index", self.ready)); - } - if self.awaiting_proxy > 0 { - // Not a blocker any more, and it must not read like one: the - // whole-library pass fetches these rather than skipping them. - s.push_str(&format!("; {} to fetch", self.awaiting_proxy)); + // One number, not two. The split used to matter because one of the + // two passes could only do the images that already had a proxy; now + // that detection refuses that proxy's size, both halves cost the same + // fetch and telling the user "169 ready, 0 to fetch" only implies a + // distinction that no longer decides anything. + let outstanding = self.ready + self.awaiting_proxy; + if outstanding > 0 { + s.push_str(&format!("; {outstanding} to index")); } s } @@ -382,6 +394,26 @@ pub fn spawn_store_face_sweep( } }; + // Nothing below this point can succeed, so do not pretend to try. + // + // This pass detects on what the store holds, and the store's largest + // tier is `FACE_TIER` — 1024, which is below the floor. Left to run it + // would fetch nothing, decode every proxy it has, and report every + // single image as failed: twenty thousand refusals that all say the + // same thing. The pass is not repairable here either, because there is + // no larger tier for it to read; the pixels it needs have to come off + // the server, which is `library::spawn_face_sweep`'s job. + if (FACE_TIER.edge()) < dr_face::MIN_DETECT_EDGE { + log::warn!( + "face sweep: the local store's largest tier is {}px, below the {}px \ + detection floor — use the library pass, which fetches its own pixels", + FACE_TIER.edge(), + dr_face::MIN_DETECT_EDGE, + ); + finish_empty(&tx); + return; + } + let wanted = match faces_outstanding(&catalog, &store, &model_id) { Ok(w) => w, Err(e) => {