(image_path: Path)
| 20413 | ) |
| 20414 | |
| 20415 | def probe_image_metadata(image_path: Path): |
| 20416 | base = { |
| 20417 | "file_name": image_path.name, |
| 20418 | "file_path": str(image_path), |
| 20419 | "file_size_bytes": image_path.stat().st_size, |
| 20420 | "file_size_mb": round(image_path.stat().st_size / (1024 * 1024), 4), |
| 20421 | "hashes": _compute_hashes(image_path) |
| 20422 | } |
| 20423 | stat = image_path.stat() |
| 20424 | base.update({ |
| 20425 | "created_utc": datetime.fromtimestamp(stat.st_ctime, tz=timezone.utc).isoformat(), |
| 20426 | "modified_utc": datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc).isoformat(), |
| 20427 | }) |
| 20428 | try: |
| 20429 | from PIL import Image, ExifTags |
| 20430 | except Exception: |
| 20431 | _install_python_package("Pillow") |
| 20432 | from PIL import Image, ExifTags |
| 20433 | |
| 20434 | def _safe(v): |
| 20435 | if v is None: |
| 20436 | return None |
| 20437 | if isinstance(v, bytes): |
| 20438 | try: |
| 20439 | return v.decode("utf-8", errors="replace") |
| 20440 | except Exception: |
| 20441 | return str(v) |
| 20442 | if isinstance(v, (str, int, float, bool)): |
| 20443 | return str(v) if isinstance(v, (str, bool)) else v |
| 20444 | if hasattr(v, "printable"): |
| 20445 | return str(v.printable) |
| 20446 | if hasattr(v, "num") and hasattr(v, "den"): |
| 20447 | try: |
| 20448 | return float(v.num) / float(v.den) |
| 20449 | except Exception: |
| 20450 | pass |
| 20451 | if isinstance(v, tuple): |
| 20452 | return [str(x) for x in v] |
| 20453 | return str(v) |
| 20454 | |
| 20455 | def _to_decimal_degrees(value, reference): |
| 20456 | try: |
| 20457 | vals = value.values if hasattr(value, "values") else value |
| 20458 | if not isinstance(vals, (tuple, list)) or len(vals) != 3: |
| 20459 | return None |
| 20460 | d = float(vals[0].num) / float(vals[0].den) |
| 20461 | m = float(vals[1].num) / float(vals[1].den) |
| 20462 | s = float(vals[2].num) / float(vals[2].den) |
| 20463 | result = d + (m / 60.0) + (s / 3600.0) |
| 20464 | if str(reference).upper() in ["S", "W"]: |
| 20465 | result *= -1 |
| 20466 | return result |
| 20467 | except Exception: |
| 20468 | return None |
| 20469 | |
| 20470 | try: |
| 20471 | import exifread |
| 20472 | except Exception: |
no test coverage detected