from photoflow.immich import pipeline APPLYABLE = ("approved", "non_trip", "skipped") def _apply_tag(client, store, asset_ids, action, tag, tag_id): todo = [a for a in asset_ids if not store.already_applied(a, action, tag)] if not todo: return [], [] try: client.tag_assets(tag_id, todo) except Exception as e: # noqa: BLE001 — recorded, surfaced for a in todo: store.log_writeback(a, action, tag, f"error:{e}") return [], [(a, str(e)) for a in todo] for a in todo: store.log_writeback(a, action, tag, "ok") return todo, [] def apply_cluster(client, store, cluster_id) -> dict: c = store.get_cluster(cluster_id) if c is None or c.status not in APPLYABLE: return {"cluster_id": cluster_id, "status": c.status if c else None, "succeeded": [], "failed": []} included = [a.immich_id for a, m in store.cluster_members(cluster_id) if m.included] succeeded, failed = [], [] if c.status == "approved": tag = c.decided_name or c.suggested_name ok, fail = _apply_tag(client, store, included, "trip", tag, client.upsert_tag(tag)) succeeded += ok failed += fail elif c.status == "non_trip": ok, fail = _apply_tag(client, store, included, "non-trip", pipeline.NON_TRIP, client.upsert_tag(pipeline.NON_TRIP)) succeeded += ok failed += fail # 'skipped': no content/non-trip tag, only processed below. failed_ids = {i for i, _ in failed} proc_targets = [a for a in included if a not in failed_ids] if proc_targets: _apply_tag(client, store, proc_targets, "processed", pipeline.PROCESSED, client.upsert_tag(pipeline.PROCESSED)) for a in proc_targets: if store.already_applied(a, "processed", pipeline.PROCESSED): store.mark_processed(a) return {"cluster_id": cluster_id, "status": c.status, "succeeded": succeeded, "failed": failed} def apply_all(client, store) -> list: return [apply_cluster(client, store, c.id) for c in store.all_clusters() if c.status in APPLYABLE]