Update cli.py

This commit is contained in:
2026-06-11 01:32:36 -04:00
committed by GitHub
parent e7ab034356
commit 3556ec54b8
+18 -34
View File
@@ -291,7 +291,7 @@ def upload_to_frigate(jobs: list[dict]) -> None:
try: try:
with open(fpath, "rb") as f: with open(fpath, "rb") as f:
resp = requests.post( resp = requests.post(
f"{frigate_url}/api/faces/train/{encoded_name}/classify", f"{frigate_url}/api/faces/{encoded_name}/register",
files={"file": (fname, f, "image/jpeg")}, files={"file": (fname, f, "image/jpeg")},
timeout=30, timeout=30,
) )
@@ -423,6 +423,14 @@ def _show_preview(jobs: list[dict]) -> None:
def _enrich_asset_with_face_data(asset: dict, person: dict) -> dict: def _enrich_asset_with_face_data(asset: dict, person: dict) -> dict:
"""Enrich an asset dict with face bounding box data from the Immich faces API.
The search/metadata endpoint does not include face bounding box data,
so we fetch it from GET /api/faces?id={asset_id} and inject it into
the asset's "people" field so process_face_mode can find it.
Returns the enriched asset dict (modifies in place and returns it).
"""
person_id = person["id"] person_id = person["id"]
face_data = fetch_face_data(asset["id"], person_id=person_id) face_data = fetch_face_data(asset["id"], person_id=person_id)
@@ -430,6 +438,7 @@ def _enrich_asset_with_face_data(asset: dict, person: dict) -> dict:
logger.debug(f"No face data returned for {person.get('name')} in asset {asset.get('id')}") logger.debug(f"No face data returned for {person.get('name')} in asset {asset.get('id')}")
return asset return asset
# Skip zero-area bounding boxes (face detection failed or no face found)
if face_data.bbox == (0, 0, 0, 0): if face_data.bbox == (0, 0, 0, 0):
logger.debug(f"Zero-area bounding box for {person.get('name')} in asset {asset.get('id')}") logger.debug(f"Zero-area bounding box for {person.get('name')} in asset {asset.get('id')}")
return asset return asset
@@ -443,11 +452,11 @@ def _enrich_asset_with_face_data(asset: dict, person: dict) -> dict:
"imageHeight": face_data.image_height, "imageHeight": face_data.image_height,
} }
# Inject into asset so process_face_mode can find it via asset["people"]
asset["people"] = [{"id": person_id, "faces": [face_info]}] asset["people"] = [{"id": person_id, "faces": [face_info]}]
return asset return asset
def execute_jobs(jobs: list[dict]) -> None: def execute_jobs(jobs: list[dict]) -> None:
"""Download and process images for all jobs.""" """Download and process images for all jobs."""
if not jobs: if not jobs:
@@ -476,10 +485,6 @@ def execute_jobs(jobs: list[dict]) -> None:
os.makedirs(person_dir, exist_ok=True) os.makedirs(person_dir, exist_ok=True)
count = 0 count = 0
skipped_download = 0
skipped_no_face = 0
skipped_other = 0
for asset in assets: for asset in assets:
try: try:
# For face mode, enrich the asset with face bounding box data # For face mode, enrich the asset with face bounding box data
@@ -499,9 +504,7 @@ def execute_jobs(jobs: list[dict]) -> None:
img = Image.open(BytesIO(resp.content)) if resp.ok else None img = Image.open(BytesIO(resp.content)) if resp.ok else None
if img is None: if img is None:
skipped_download += 1 progress.console.print(f"[red]Failed download {asset['id']}[/red]")
progress.console.print(f"[red]✗ Failed download {asset['id']}[/red]")
logger.debug(f"Image download failed for asset {asset['id']}")
else: else:
saved = ( saved = (
process_face_mode(img, asset, person, person_dir, count) process_face_mode(img, asset, person, person_dir, count)
@@ -512,22 +515,10 @@ def execute_jobs(jobs: list[dict]) -> None:
) )
if saved: if saved:
count += 1 count += 1
logger.debug(f"Saved image #{count} from asset {asset['id']}")
else: else:
if mode == "face": progress.console.print(
skipped_no_face += 1 f"[yellow]Skipped {asset['id']} (no usable face data)[/yellow]"
progress.console.print( )
f"[yellow]⏭ No usable face data for {asset['id']}[/yellow]"
)
logger.debug(
f"process_face_mode returned False for asset {asset['id']} — "
f"people={asset.get('people', 'MISSING')}"
)
else:
skipped_other += 1
progress.console.print(
f"[yellow]⏭ Skipped {asset['id']}[/yellow]"
)
except Exception as e: except Exception as e:
logger.error(f"Failed to process asset {asset['id']}: {e}") logger.error(f"Failed to process asset {asset['id']}: {e}")
@@ -536,15 +527,9 @@ def execute_jobs(jobs: list[dict]) -> None:
progress.remove_task(job_task) progress.remove_task(job_task)
# Per-person execution summary # Log how many images were actually saved vs selected
rprint( if count < len(assets):
f"\n [bold]{name}:[/bold] saved {count}/{len(assets)} images " logger.info(f"{name}: saved {count}/{len(assets)} selected images")
f"(download_failed={skipped_download}, no_face_data={skipped_no_face}, other={skipped_other})"
)
logger.info(
f"{name}: saved {count}/{len(assets)} "
f"(download_failed={skipped_download}, no_face_data={skipped_no_face}, other={skipped_other})"
)
def main() -> None: def main() -> None:
@@ -597,4 +582,3 @@ def main() -> None:
if __name__ == "__main__": if __name__ == "__main__":
main() main()