US07-03: Harden Media and Metadata Edge Cases

This commit is contained in:
2026-08-17 00:56:39 +02:00
parent d2e44b5657
commit 691bd45825
18 changed files with 1525 additions and 53 deletions

View File

@@ -56,6 +56,30 @@ def read_keyword_sets(paths: Iterable[str]) -> dict[str, set[str]]:
return out
def read_all(path: str) -> dict | None:
"""Every tag exiftool can read from ``path``, or ``None`` when it cannot answer.
This is the snapshot an EXIF checkpoint compares against: proving that a write
preserved the fields it does not own requires knowing all of them, not just the
ones being written (US07-03). ``None`` (exiftool missing, unreadable file,
unparsable output) is not an empty snapshot — a caller must not read it as
"nothing was there".
"""
try:
result = subprocess.run(
["exiftool", "-m", "-j", "-G0:1", path], capture_output=True, text=True
)
except FileNotFoundError:
return None
try:
records = json.loads(result.stdout or "[]")
except ValueError:
return None
if not records:
return None
return {k: v for k, v in records[0].items() if k != "SourceFile"}
def apply_keywords(path: str, *, add: Iterable[str] = (), remove: Iterable[str] = ()) -> bool:
"""Idempotently add/remove keywords in Keywords + Subject; preserve all else."""
args = ["exiftool", "-m", "-overwrite_original"]