refactor: collapse Config proxy, migrate tracker to SQLite, split reconcile module

- Config: remove _ConfigAccessor and ConfigManager; use __getattr__ for lazy
  loading on single _Config class; re-register self as _instance in __getattr__
  so reset() always clears the correct object (item 1)
- upload_tracker: replace hand-rolled JSON store with sqlite3; auto-migrates
  existing JSON on first run; remove dead record_frigate_file function;
  connection re-opens when CACHE_DIR changes for test isolation (items 2, 8)
- diversity: move ThreadPoolExecutor import to module level; inject optional
  fetch_fn parameter for testability (items 3, 6)
- pyproject: consolidate 4 variant files into extras (gpu/rocm/intel/cpu);
  update Dockerfile to use --extra flag; delete variant pyproject/lock files;
  uv.lock needs regen with `uv lock` after this change (item 4)
- jobs: extract _build_job helper to separate business logic from terminal I/O;
  auto_configure delegates dedup/selection to _build_job (item 5)
- logging: convert f-string log calls to % interpolation throughout all winnow/
  modules (item 7)
- reconcile: new module with reconcile_frigate_mappings and
  enrich_asset_with_face_data extracted from executor.py (item 9)
- scheduler: print next scheduled run time after startup and after each run;
  fix f-string logger.error call (item 10)
This commit is contained in:
2026-06-14 19:59:17 +00:00
parent ad1fbd4c2a
commit e2a1924fb0
22 changed files with 754 additions and 6613 deletions
+6 -6
View File
@@ -36,7 +36,7 @@ def _get_faces_data() -> dict | None:
resp.raise_for_status()
return resp.json()
except Exception as e:
logger.warning(f"Could not query Frigate faces API: {e}")
logger.warning("Could not query Frigate faces API: %s", e)
return None
@@ -125,7 +125,7 @@ def recognize_face(file_path: str) -> tuple[str | None, float] | None:
return (data.get("face_name"), round(float(data["score"]), 4))
return None
except Exception as e:
logger.debug(f"Frigate recognize failed for {file_path}: {e}")
logger.debug("Frigate recognize failed for %s: %s", file_path, e)
return None
@@ -147,15 +147,15 @@ def delete_frigate_person_files(person_name: str, filenames: list[str]) -> bool:
timeout=10,
)
if resp.ok:
logger.debug(f"Deleted {len(filenames)} Frigate file(s) for {person_name}")
logger.debug("Deleted %s Frigate file(s) for %s", len(filenames), person_name)
return True
if resp.status_code == 404:
# File already absent — stale tracker entry. Return True so the caller
# removes it from the tracker and frees the slot cleanly.
logger.warning(f"Frigate file(s) not found for {person_name} (stale tracker entry?): {filenames}")
logger.warning("Frigate file(s) not found for %s (stale tracker entry?): %s", person_name, filenames)
return True
logger.warning(f"Frigate delete returned {resp.status_code} for {person_name}")
logger.warning("Frigate delete returned %s for %s", resp.status_code, person_name)
return False
except Exception as e:
logger.warning(f"Failed to delete Frigate files for {person_name}: {e}")
logger.warning("Failed to delete Frigate files for %s: %s", person_name, e)
return False