US06-03: Preserve Offline Identity and Review Evidence (#79)
This commit was merged in pull request #79.
This commit is contained in:
@@ -60,8 +60,10 @@ from photo_pipeline.services.archive_journal import (
|
||||
ArchiveState,
|
||||
)
|
||||
from photo_pipeline.services.archives import MARKER_NAME, ArchiveError, ArchiveService
|
||||
from photo_pipeline.services.duplicates import DuplicateService
|
||||
from photo_pipeline.services.hashing import sha256_file
|
||||
from photo_pipeline.services.rename_apply import PreconditionFailed, maybe_fault
|
||||
from photo_pipeline.services.thumbnails import ThumbnailService
|
||||
|
||||
# The per-medium manifest: one JSON line per archived file, appended and fsynced
|
||||
# before its source is removed. It lives with the bytes so the archive can still be
|
||||
@@ -335,6 +337,7 @@ class ArchiveTransferService:
|
||||
raise PreconditionFailed(
|
||||
"archive_unverified", f"{destination} is not a verified archive copy"
|
||||
)
|
||||
self._require_evidence(operation["asset_id"], destination)
|
||||
if source.exists():
|
||||
if source.is_symlink():
|
||||
raise PreconditionFailed("symlink", f"{source} became a symlink")
|
||||
@@ -377,6 +380,28 @@ class ArchiveTransferService:
|
||||
"asset_moved", f"asset {operation['asset_id']} is no longer at {source}"
|
||||
)
|
||||
|
||||
def _require_evidence(self, asset_id: str, source: Path) -> dict:
|
||||
"""Review evidence must be durable before the original goes.
|
||||
|
||||
The perceptual hash keeps the asset in the fuzzy index once its bytes are
|
||||
unreachable, and the protected preview is what duplicate review can still
|
||||
look at. Both are read from the freshly verified archive copy, which holds
|
||||
exactly the bytes being archived. A file that cannot be decoded has neither
|
||||
— recorded, not fatal, since its exact hashes remain — but failing to
|
||||
produce a preview from a decodable original stops the removal (concept §9).
|
||||
"""
|
||||
DuplicateService(self._session_factory).ensure_phash(asset_id, source=source)
|
||||
preview = ThumbnailService(self._session_factory, self._config).ensure_protected(
|
||||
asset_id, source=source
|
||||
)
|
||||
if preview["state"] == "unavailable":
|
||||
raise PreconditionFailed(
|
||||
"preview_unavailable",
|
||||
f"no durable comparison preview for asset {asset_id} "
|
||||
f"({preview['error_code']})",
|
||||
)
|
||||
return preview
|
||||
|
||||
# ── database ──────────────────────────────────────────────────────────────
|
||||
|
||||
def _record_archived(self, operation: dict, location: dict, destination: Path) -> None:
|
||||
|
||||
Reference in New Issue
Block a user