Face Recognition / People Integration #61
@@ -415,22 +415,36 @@ fn try_auto_bind(
|
|||||||
/// Pulled out for unit testing — the same `PathExcluder` /memories uses,
|
/// Pulled out for unit testing — the same `PathExcluder` /memories uses,
|
||||||
/// just applied at the face-detect candidate set instead of the memories
|
/// just applied at the face-detect candidate set instead of the memories
|
||||||
/// listing. Skip @eaDir / .thumbnails / user-defined paths before we burn
|
/// listing. Skip @eaDir / .thumbnails / user-defined paths before we burn
|
||||||
/// a detect call (and Apollo's GPU memory) on junk.
|
/// a detect call (and Apollo's GPU memory) on junk. Also drops anything
|
||||||
|
/// that isn't an image file — the backlog drain pulls every hashed row in
|
||||||
|
/// `image_exif`, which includes videos; sending those to Apollo just
|
||||||
|
/// produces `failed` markers and inflates the FAILED stat.
|
||||||
pub(crate) fn filter_excluded(
|
pub(crate) fn filter_excluded(
|
||||||
base: &Path,
|
base: &Path,
|
||||||
excluded_dirs: &[String],
|
excluded_dirs: &[String],
|
||||||
candidates: Vec<FaceCandidate>,
|
candidates: Vec<FaceCandidate>,
|
||||||
library_name: Option<&str>,
|
library_name: Option<&str>,
|
||||||
) -> Vec<FaceCandidate> {
|
) -> Vec<FaceCandidate> {
|
||||||
if excluded_dirs.is_empty() {
|
let excluder = if excluded_dirs.is_empty() {
|
||||||
return candidates;
|
None
|
||||||
}
|
} else {
|
||||||
let excluder = PathExcluder::new(base, excluded_dirs);
|
Some(PathExcluder::new(base, excluded_dirs))
|
||||||
|
};
|
||||||
candidates
|
candidates
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|c| {
|
.filter(|c| {
|
||||||
let abs = base.join(&c.rel_path);
|
let abs = base.join(&c.rel_path);
|
||||||
if excluder.is_excluded(&abs) {
|
if !file_types::is_image_file(&abs) {
|
||||||
|
debug!(
|
||||||
|
"face_watch: skipping non-image path {} (library {})",
|
||||||
|
c.rel_path,
|
||||||
|
library_name.unwrap_or("<unknown>")
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if let Some(ex) = excluder.as_ref()
|
||||||
|
&& ex.is_excluded(&abs)
|
||||||
|
{
|
||||||
debug!(
|
debug!(
|
||||||
"face_watch: skipping excluded path {} (library {})",
|
"face_watch: skipping excluded path {} (library {})",
|
||||||
c.rel_path,
|
c.rel_path,
|
||||||
@@ -507,8 +521,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn filter_excluded_empty_rules_passes_all() {
|
fn filter_excluded_empty_rules_passes_all() {
|
||||||
// Skip the PathExcluder build entirely on the common path where
|
// EXCLUDED_DIRS unset still lets every image through — only the
|
||||||
// EXCLUDED_DIRS is unset — saves an allocation per pass.
|
// PathExcluder is skipped, the image-extension gate still runs.
|
||||||
let tmp = tempfile::tempdir().unwrap();
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
let base = tmp.path();
|
let base = tmp.path();
|
||||||
let candidates = vec![cand("a.jpg"), cand("b.jpg")];
|
let candidates = vec![cand("a.jpg"), cand("b.jpg")];
|
||||||
@@ -516,6 +530,25 @@ mod tests {
|
|||||||
assert_eq!(kept.len(), 2);
|
assert_eq!(kept.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn filter_excluded_drops_videos_and_non_media() {
|
||||||
|
// Backlog drain pulls every hashed row in image_exif (videos
|
||||||
|
// included). Videos must never reach Apollo — opencv can't
|
||||||
|
// decode them, every call would 422 and write a `failed` marker.
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let base = tmp.path();
|
||||||
|
let candidates = vec![
|
||||||
|
cand("photos/a.jpg"),
|
||||||
|
cand("photos/clip.mp4"),
|
||||||
|
cand("photos/clip.MOV"),
|
||||||
|
cand("photos/notes.txt"),
|
||||||
|
cand("photos/b.heic"),
|
||||||
|
];
|
||||||
|
let kept = filter_excluded(base, &[], candidates, Some("test"));
|
||||||
|
let kept_paths: Vec<_> = kept.iter().map(|c| c.rel_path.as_str()).collect();
|
||||||
|
assert_eq!(kept_paths, vec!["photos/a.jpg", "photos/b.heic"]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn read_bytes_passes_through_for_jpeg() {
|
fn read_bytes_passes_through_for_jpeg() {
|
||||||
// JPEG goes through plain read — we DON'T want to lose orientation
|
// JPEG goes through plain read — we DON'T want to lose orientation
|
||||||
|
|||||||
Reference in New Issue
Block a user