Added date_modified to PhotoInfo
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.22.5"
|
||||
__version__ = "0.22.6"
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"}]
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
29
tests/test_modified_date_catalina_10_15_1.py
Normal file
29
tests/test_modified_date_catalina_10_15_1.py
Normal 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
|
||||
|
||||
30
tests/test_modified_date_mojave_10_14_6.py
Normal file
30
tests/test_modified_date_mojave_10_14_6.py
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user