Update cli.py
This commit is contained in:
+18
-34
@@ -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()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user