Refactor photo metadata workflow

This commit is contained in:
ajp_anton
2026-08-24 16:34:20 +00:00
parent 100cad1cb5
commit eb5937a629
15 changed files with 2111 additions and 1727 deletions
+63
View File
@@ -0,0 +1,63 @@
"""Small, shared helpers for values returned by ExifTool."""
from __future__ import annotations
import math
import re
from collections.abc import Iterable, Mapping
from datetime import datetime, timedelta, timezone
DATETIME_RE = re.compile(
r"(?P<Y>\d{4})[:\-]?(?P<M>\d{2})[:\-]?(?P<D>\d{2})"
r"(?:[ T_])?"
r"(?P<h>\d{2}):?(?P<m>\d{2}):?(?P<s>\d{2})"
r"(?:[.,](?P<sub>\d+))?"
r"(?:\s*(?P<tz>Z|[+-]\d{2}:?\d{2}))?"
)
def first_tag(metadata: Mapping[str, object], tags: Iterable[str]) -> object | None:
for tag in tags:
if tag in metadata:
return metadata[tag]
return None
def tag_value(metadata: Mapping[str, object], tag: str) -> str | None:
value = metadata.get(tag)
return None if value is None else str(value)
def parse_exif_datetime(value: object, assume_utc: bool = False) -> datetime | None:
if value is None:
return None
match = DATETIME_RE.search(str(value).strip())
if not match:
return None
try:
tz_text = match.group("tz")
if tz_text == "Z":
tzinfo = timezone.utc
elif tz_text:
sign = 1 if tz_text[0] == "+" else -1
digits = tz_text[1:].replace(":", "")
tzinfo = timezone(sign * timedelta(hours=int(digits[:2]), minutes=int(digits[2:])))
else:
tzinfo = None
parsed = datetime(
int(match.group("Y")),
int(match.group("M")),
int(match.group("D")),
int(match.group("h")),
int(match.group("m")),
int(match.group("s")),
tzinfo=tzinfo,
)
return parsed.replace(tzinfo=timezone.utc) if assume_utc and tzinfo is None else parsed
except ValueError:
return None
def round_half_up(value: float) -> int:
return math.floor(value + 0.5)