Files
osxphotos/osxphotos/compare_exif.py
Rhet Turnbull dc4d322dab Feature timewarp (#675)
* Implemented timewarp command

* Updated docs

* Added missing pytest mark
2022-05-01 10:01:05 -07:00

168 lines
4.9 KiB
Python

""" PhotoCompare class to compare date/time/timezone in Photos to the exif data """
from collections import namedtuple
from typing import Callable, List, Optional, Tuple
from osxphotos import PhotosDB
from osxphotos.exiftool import ExifTool
from photoscript import Photo
from .datetime_utils import datetime_naive_to_local, datetime_to_new_tz
from .exif_datetime_updater import get_exif_date_time_offset
from .phototz import PhotoTimeZone
from .utils import noop
ExifDiff = namedtuple(
"ExifDiff",
[
"diff",
"photos_date",
"photos_time",
"photos_tz",
"exif_date",
"exif_time",
"exif_tz",
],
)
def change(msg: str) -> str:
"""Add change tag to string"""
return f"[change]{msg}[/change]"
def no_change(msg: str) -> str:
"""Add no change tag to string"""
return f"[no_change]{msg}[/no_change]"
class PhotoCompare:
"""Class to compare date/time/timezone in Photos to the exif data"""
def __init__(
self,
library_path: Optional[str] = None,
verbose: Optional[Callable] = None,
exiftool_path: Optional[str] = None,
):
self.library_path = library_path
self.db = PhotosDB(self.library_path)
self.verbose = verbose or noop
self.exiftool_path = exiftool_path
self.phototz = PhotoTimeZone(self.library_path)
def compare_exif(self, photo: Photo) -> List[str]:
"""Compare date/time/timezone in Photos to the exif data
Args:
photo (Photo): Photo object to compare
Returns:
List of strings:
"""
photos_offset_seconds, photos_tz_str, _ = self.phototz.get_timezone(photo)
photos_date = datetime_naive_to_local(photo.date)
photos_date = datetime_to_new_tz(photos_date, photos_offset_seconds)
photos_date_str = photos_date.strftime("%Y-%m-%d %H:%M:%S")
photo_ = self.db.get_photo(photo.uuid)
if photo_path := photo_.path:
exif = ExifTool(filepath=photo_path, exiftool=self.exiftool_path)
exif_dict = exif.asdict()
exif_dt_offset = get_exif_date_time_offset(exif_dict)
exif_offset = exif_dt_offset.offset_str
exif_date = (
exif_dt_offset.datetime.strftime("%Y-%m-%d %H:%M:%S")
if exif_dt_offset.datetime
else ""
)
else:
exif_date = ""
exif_offset = ""
return [photos_date_str, photos_tz_str, exif_date, exif_offset]
def compare_exif_with_markup(self, photo: Photo) -> ExifDiff:
"""Compare date/time/timezone in Photos to the exif data and return an ExifDiff named tuple;
adds rich markup to strings to show differences
Args:
photo (Photo): Photo object to compare
"""
photos_date, photos_tz, exif_date, exif_tz = self.compare_exif(photo)
diff = False
photos_date, photos_time = photos_date.split(" ", 1)
try:
exif_date, exif_time = exif_date.split(" ", 1)
except ValueError:
exif_date = exif_date
exif_time = ""
if photos_date != exif_date:
photos_date = change(photos_date)
exif_date = change(exif_date)
diff = True
else:
photos_date = no_change(photos_date)
exif_date = no_change(exif_date)
if photos_time != exif_time:
photos_time = change(photos_time)
exif_time = change(exif_time)
diff = True
else:
photos_time = no_change(photos_time)
exif_time = no_change(exif_time)
if photos_tz != exif_tz:
photos_tz = change(photos_tz)
exif_tz = change(exif_tz)
diff = True
else:
photos_tz = no_change(photos_tz)
exif_tz = no_change(exif_tz)
return ExifDiff(
diff,
photos_date,
photos_time,
photos_tz,
exif_date,
exif_time,
exif_tz,
)
def compare_exif_no_markup(self, photo: Photo) -> ExifDiff:
"""Compare date/time/timezone in Photos to the exif data and return an ExifDiff named tuple;
Args:
photo (Photo): Photo object to compare
"""
photos_date, photos_tz, exif_date, exif_tz = self.compare_exif(photo)
diff = False
photos_date, photos_time = photos_date.split(" ", 1)
try:
exif_date, exif_time = exif_date.split(" ", 1)
except ValueError:
exif_date = exif_date
exif_time = ""
if photos_date != exif_date:
diff = True
if photos_time != exif_time:
diff = True
if photos_tz != exif_tz:
diff = True
return ExifDiff(
diff,
photos_date,
photos_time,
photos_tz,
exif_date,
exif_time,
exif_tz,
)