Added date_modified to PhotoInfo

This commit is contained in:
Rhet Turnbull
2020-01-25 08:07:50 -08:00
parent fd8d466e05
commit 67b0ae0bf6
9 changed files with 110 additions and 27 deletions

View File

@@ -544,7 +544,10 @@ Returns the current filename of the photo on disk. See also [original_filename]
Returns the original filename of the photo when it was imported to Photos. **Note**: Photos 5.0+ renames the photo when it adds the file to the library using UUID. See also [filename](#filename)
#### `date`
Returns the date of the photo as a datetime.datetime object
Returns the create date of the photo as a datetime.datetime object
#### `date_modified`
Returns the modification date of the photo as a datetime.datetime object or None if photo has no modification date
#### `description`
Returns the description of the photo

View File

@@ -46,7 +46,7 @@ def get_photos_db(*db_options):
click.echo(f"Using Photos library: {db}", err=True)
return db
else:
return None
return None
# Click CLI object & context settings
@@ -863,9 +863,11 @@ def print_photo_info(photos, json=False):
"path_live_photo",
"iscloudasset",
"incloud",
"date_modified",
]
)
for p in photos:
date_modified_iso = p.date_modified.isoformat() if p.date_modified else None
dump.append(
[
p.uuid,
@@ -895,6 +897,7 @@ def print_photo_info(photos, json=False):
p.path_live_photo,
p.iscloudasset,
p.incloud,
date_modified_iso,
]
)
for row in dump:

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.22.5"
__version__ = "0.22.6"

View File

@@ -64,6 +64,20 @@ class PhotoInfo:
imagedate_utc = imagedate.astimezone(tz=tz)
return imagedate_utc
@property
def date_modified(self):
""" image modification date as timezone aware datetime object
or None if no modification date set """
imagedate = self._info["lastmodifieddate"]
if imagedate:
seconds = self._info["imageTimeZoneOffsetSeconds"] or 0
delta = timedelta(seconds=seconds)
tz = timezone(delta)
imagedate_utc = imagedate.astimezone(tz=tz)
return imagedate_utc
else:
return None
@property
def tzoffset(self):
""" timezone offset from UTC in seconds """
@@ -630,6 +644,9 @@ class PhotoInfo:
exif["DateTimeOriginal"] = datetimeoriginal
exif["OffsetTimeOriginal"] = offsettime
if self.date_modified is not None:
exif["ModifyDate"] = self.date_modified.strftime("%Y:%m:%d %H:%M:%S")
json_str = json.dumps([exif])
return json_str
@@ -660,11 +677,16 @@ class PhotoInfo:
return f"osxphotos.{self.__class__.__name__}(db={self._db}, uuid='{self._uuid}', info={self._info})"
def __str__(self):
""" string representation of PhotoInfo object """
date_iso = self.date.isoformat()
date_modified_iso = self.date_modified.isoformat() if self.date_modified else None
info = {
"uuid": self.uuid,
"filename": self.filename,
"original_filename": self.original_filename,
"date": str(self.date),
"date": date_iso,
"description": self.description,
"title": self.title,
"keywords": self.keywords,
@@ -688,11 +710,15 @@ class PhotoInfo:
"path_live_photo": self.path_live_photo,
"iscloudasset": self.iscloudasset,
"incloud": self.incloud,
"date_modified": date_modified_iso,
}
return yaml.dump(info, sort_keys=False)
def json(self):
""" return JSON representation """
date_modified_iso = self.date_modified.isoformat() if self.date_modified else None
pic = {
"uuid": self.uuid,
"filename": self.filename,
@@ -721,6 +747,7 @@ class PhotoInfo:
"path_live_photo": self.path_live_photo,
"iscloudasset": self.iscloudasset,
"incloud": self.incloud,
"date_modified": date_modified_iso,
}
return json.dumps(pic)

View File

@@ -94,7 +94,7 @@ class PhotosDB:
if dbfile:
# shouldn't pass via both *args and dbfile=
raise TypeError(
f"photos database path must be specified as argument or named parameter dbfile but not both: args: {args}, dbfile: {dbfile}",
f"photos database path must be specified as argument or named parameter dbfile but not both: args: {dbfile_}, dbfile: {dbfile}",
dbfile_,
dbfile,
)
@@ -532,19 +532,10 @@ class PhotosDB:
self._dbphotos[uuid]["modelID"] = row[1]
self._dbphotos[uuid]["masterUuid"] = row[2]
self._dbphotos[uuid]["filename"] = row[3]
try:
self._dbphotos[uuid]["lastmodifieddate"] = datetime.fromtimestamp(
row[4] + td
)
except:
self._dbphotos[uuid]["lastmodifieddate"] = datetime.fromtimestamp(
row[5] + td
)
self._dbphotos[uuid]["imageDate"] = datetime.fromtimestamp(
row[5] + td
) # - row[9], timezone.utc)
self._dbphotos[uuid]["lastmodifieddate"] = (
datetime.fromtimestamp(row[4] + td) if row[4] is not None else None
)
self._dbphotos[uuid]["imageDate"] = datetime.fromtimestamp(row[5] + td)
self._dbphotos[uuid]["mainRating"] = row[6]
self._dbphotos[uuid]["hasAdjustments"] = row[7]
self._dbphotos[uuid]["hasKeywords"] = row[8]
@@ -647,7 +638,6 @@ class PhotosDB:
JOIN RKModelResource on RKModelResource.attachedModelId = RKVersion.modelId
WHERE RKVersion.isInTrash = 0 """
)
# get info on path of live photo movie
# Order of results:
# 0 RKVersion.uuid
@@ -1008,11 +998,9 @@ class PhotosDB:
info["masterUuid"] = None
info["masterFingerprint"] = row[1]
info["name"] = row[2]
try:
info["lastmodifieddate"] = datetime.fromtimestamp(row[4] + td)
except:
info["lastmodifieddate"] = datetime.fromtimestamp(row[5] + td)
info["lastmodifieddate"] = (
datetime.fromtimestamp(row[4] + td) if row[4] is not None else None
)
info["imageDate"] = datetime.fromtimestamp(row[5] + td)
info["imageTimeZoneOffsetSeconds"] = row[6]
info["hidden"] = row[9]

View File

@@ -455,8 +455,9 @@ def test_exiftool_json_sidecar():
"GPSLongitude": "0 deg 7\' 54.50\\" W",
"GPSPosition": "51 deg 30\' 12.86\\" N, 0 deg 7\' 54.50\\" W",
"GPSLatitudeRef": "North", "GPSLongitudeRef": "West",
"DateTimeOriginal": "2018:10:13 09:18:12", "OffsetTimeOriginal": "-04:00"}]
"""
"DateTimeOriginal": "2018:10:13 09:18:12",
"OffsetTimeOriginal": "-04:00",
"ModifyDate": "2019:12:08 14:06:44"}] """
)
json_got = photos[0]._exiftool_json_sidecar()

View File

@@ -397,7 +397,9 @@ def test_exiftool_json_sidecar():
"GPSLongitude": "0 deg 7\' 54.50\\" W",
"GPSPosition": "51 deg 30\' 12.86\\" N, 0 deg 7\' 54.50\\" W",
"GPSLatitudeRef": "North", "GPSLongitudeRef": "West",
"DateTimeOriginal": "2018:10:13 09:18:12", "OffsetTimeOriginal": "-04:00"}]
"DateTimeOriginal": "2018:10:13 09:18:12",
"OffsetTimeOriginal": "-04:00",
"ModifyDate": "2019:12:01 11:43:45"}]
"""
)

View File

@@ -0,0 +1,29 @@
import pytest
PHOTOS_DB = "./tests/Test-Shared-10.15.1.photoslibrary/database/photos.db"
PHOTOS_DB_PATH = "/Test-Shared-10.15.1.photoslibrary/database/photos.db"
PHOTOS_LIBRARY_PATH = "/Test-Shared-10.15.1.photoslibrary"
UUID_DICT = {
"modified": "37210110-E940-4227-92D3-45C40F68EB0A",
"not_modified": "35243F7D-88C4-4408-B516-C74406E90C15",
}
def test_modified():
import datetime
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["modified"]])
assert photos[0].date_modified is not None
assert photos[0].date_modified.isoformat() == "2019-12-26T21:08:48.306538-07:00"
def test_not_modified():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["not_modified"]])
assert photos[0].date_modified is None

View File

@@ -0,0 +1,30 @@
import pytest
PHOTOS_DB = "./tests/Test-10.14.6.photoslibrary/database/photos.db"
PHOTOS_DB_PATH = "/Test-10.14.6.photoslibrary/database/photos.db"
PHOTOS_LIBRARY_PATH = "/Test-Shared-10.14.6.photoslibrary"
UUID_DICT = {
"modified": "3Jn73XpSQQCluzRBMWRsMA",
"not_modified": "35243F7D-88C4-4408-B516-C74406E90C15",
}
def test_modified():
import datetime
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["modified"]])
assert photos[0].date_modified is not None
assert photos[0].date_modified.isoformat() == "2019-12-01T11:43:45.714123-04:00"
# no non-modified photos in the 10.14.6 database
# def test_not_modified():
# import osxphotos
# photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
# photos = photosdb.photos(uuid=[UUID_DICT["not_modified"]])
# assert photos[0].date_modified is None