diff --git a/README.md b/README.md index 6eeeb0dc..7aa8594e 100644 --- a/README.md +++ b/README.md @@ -1048,6 +1048,27 @@ Returns the absolute path to the edited photo on disk as a string. If the photo **Note**: will also return None if the edited photo is missing on disk. +#### `height` +Returns height of the photo in pixels. If image has been edited, returns height of the edited image, otherwise returns height of the original image. See also [original_height](#original_height). + +#### `width` +Returns width of the photo in pixels. If image has been edited, returns width of the edited image, otherwise returns width of the original image. See also [original_width](#original_width). + +#### `orientation` +Returns EXIF orientation value of the photo as integer. If image has been edited, returns orientation of the edited image, otherwise returns orientation of the original image. See also [original_orientation](#original_orientation). + +#### `original_height` +Returns height of the original photo in pixels. See also [height](#height). + +#### `original_width` +Returns width of the original photo in pixels. See also [width](#width). + +#### `original_orientation` +Returns EXIF orientation value of the original photo as integer. See also [orientation](#orientation). + +#### `original_filesize` +Returns size of the original photo in bytes as integer. + #### `ismissing` Returns `True` if the original image file is missing on disk, otherwise `False`. This can occur if the file has been uploaded to iCloud but not yet downloaded to the local library or if the file was deleted or imported from a disk that has been unmounted and user hasn't enabled "Copy items to the Photos library" in Photos preferences. **Note**: this status is computed based on data in the Photos library and `ismissing` does not verify if the photo is actually missing. See also [path](#path). diff --git a/osxphotos/_version.py b/osxphotos/_version.py index e522448a..d420ed61 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,3 +1,3 @@ """ version info """ -__version__ = "0.30.4" +__version__ = "0.30.5" diff --git a/osxphotos/photoinfo/photoinfo.py b/osxphotos/photoinfo/photoinfo.py index 4e8eac1b..93f50f1b 100644 --- a/osxphotos/photoinfo/photoinfo.py +++ b/osxphotos/photoinfo/photoinfo.py @@ -642,6 +642,41 @@ class PhotoInfo: otherwise returns False """ return self._info["raw_is_original"] + @property + def height(self): + """ returns height of the current photo version in pixels """ + return self._info["height"] + + @property + def width(self): + """ returns width of the current photo version in pixels """ + return self._info["width"] + + @property + def orientation(self): + """ returns EXIF orientation of the current photo version as int """ + return self._info["orientation"] + + @property + def original_height(self): + """ returns height of the original photo version in pixels """ + return self._info["original_height"] + + @property + def original_width(self): + """ returns width of the original photo version in pixels """ + return self._info["original_width"] + + @property + def original_orientation(self): + """ returns EXIF orientation of the original photo version as int """ + return self._info["original_orientation"] + + @property + def original_filesize(self): + """ returns filesize of original photo in bytes as int """ + return self._info["original_filesize"] + def render_template( self, template_str, diff --git a/osxphotos/photosdb/photosdb.py b/osxphotos/photosdb/photosdb.py index 41cac1b7..c1e719be 100644 --- a/osxphotos/photosdb/photosdb.py +++ b/osxphotos/photosdb/photosdb.py @@ -716,7 +716,14 @@ class PhotosDB: RKVersion.rawMasterUuid, RKVersion.nonRawMasterUuid, RKMaster.alternateMasterUuid, - RKVersion.isInTrash + RKVersion.isInTrash, + RKVersion.processedHeight, + RKVersion.processedWidth, + RKVersion.orientation, + RKMaster.height, + RKMaster.width, + RKMaster.orientation, + RKMaster.fileSize FROM RKVersion, RKMaster WHERE RKVersion.masterUuid = RKMaster.uuid""" ) @@ -736,7 +743,14 @@ class PhotosDB: RKVersion.rawMasterUuid, RKVersion.nonRawMasterUuid, RKMaster.alternateMasterUuid, - RKVersion.isInTrash + RKVersion.isInTrash, + RKVersion.processedHeight, + RKVersion.processedWidth, + RKVersion.orientation, + RKMaster.height, + RKMaster.width, + RKMaster.orientation, + RKMaster.originalFileSize FROM RKVersion, RKMaster WHERE RKVersion.masterUuid = RKMaster.uuid""" ) @@ -775,6 +789,13 @@ class PhotosDB: # 30 RKVersion.nonRawMasterUuid, -- UUID of non-RAW master # 31 RKMaster.alternateMasterUuid -- UUID of alternate master (will be RAW master for JPEG and JPEG master for RAW) # 32 RKVersion.isInTrash + # 33 RKVersion.processedHeight, + # 34 RKVersion.processedWidth, + # 35 RKVersion.orientation, + # 36 RKMaster.height, + # 37 RKMaster.width, + # 38 RKMaster.orientation, + # 39 RKMaster.originalFileSize for row in c: uuid = row[0] @@ -920,6 +941,15 @@ class PhotosDB: # recently deleted items self._dbphotos[uuid]["intrash"] = True if row[32] == 1 else False + # height/width/orientation + self._dbphotos[uuid]["height"] = row[33] + self._dbphotos[uuid]["width"] = row[34] + self._dbphotos[uuid]["orientation"] = row[35] + self._dbphotos[uuid]["original_height"] = row[36] + self._dbphotos[uuid]["original_width"] = row[37] + self._dbphotos[uuid]["original_orientation"] = row[38] + self._dbphotos[uuid]["original_filesize"] = row[39] + # get additional details from RKMaster, needed for RAW processing c.execute( """ SELECT @@ -1457,7 +1487,14 @@ class PhotosDB: ZADDITIONALASSETATTRIBUTES.ZREVERSELOCATIONDATA, ZGENERICASSET.ZMOMENT, ZADDITIONALASSETATTRIBUTES.ZORIGINALRESOURCECHOICE, - ZGENERICASSET.ZTRASHEDSTATE + ZGENERICASSET.ZTRASHEDSTATE, + ZGENERICASSET.ZHEIGHT, + ZGENERICASSET.ZWIDTH, + ZGENERICASSET.ZORIENTATION, + ZADDITIONALASSETATTRIBUTES.ZORIGINALHEIGHT, + ZADDITIONALASSETATTRIBUTES.ZORIGINALWIDTH, + ZADDITIONALASSETATTRIBUTES.ZORIGINALORIENTATION, + ZADDITIONALASSETATTRIBUTES.ZORIGINALFILESIZE FROM ZGENERICASSET JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = ZGENERICASSET.Z_PK ORDER BY ZGENERICASSET.ZUUID """ @@ -1493,6 +1530,13 @@ class PhotosDB: # 26 ZGENERICASSET.ZMOMENT -- FK for ZMOMENT.Z_PK # 27 ZADDITIONALASSETATTRIBUTES.ZORIGINALRESOURCECHOICE -- 1 if associated RAW image is original else 0 # 28 ZGENERICASSET.ZTRASHEDSTATE -- 0 if not in trash, 1 if in trash + # 29 ZGENERICASSET.ZHEIGHT, + # 30 ZGENERICASSET.ZWIDTH, + # 31 ZGENERICASSET.ZORIENTATION, + # 32 ZADDITIONALASSETATTRIBUTES.ZORIGINALHEIGHT, + # 33 ZADDITIONALASSETATTRIBUTES.ZORIGINALWIDTH, + # 34 ZADDITIONALASSETATTRIBUTES.ZORIGINALORIENTATION, + # 35 ZADDITIONALASSETATTRIBUTES.ZORIGINALFILESIZE for row in c: uuid = row[0] @@ -1644,6 +1688,15 @@ class PhotosDB: # recently deleted items info["intrash"] = True if row[28] == 1 else False + # height/width/orientation + info["height"] = row[29] + info["width"] = row[30] + info["orientation"] = row[31] + info["original_height"] = row[32] + info["original_width"] = row[33] + info["original_orientation"] = row[34] + info["original_filesize"] = row[35] + # associated RAW image info # will be filled in later info["has_raw"] = False diff --git a/tests/test_catalina_10_15_5.py b/tests/test_catalina_10_15_5.py index 0a62a128..f4d4a782 100644 --- a/tests/test_catalina_10_15_5.py +++ b/tests/test_catalina_10_15_5.py @@ -241,6 +241,46 @@ def test_attributes(): assert p.ismissing == False +def test_attributes_2(): + """ Test attributes including height, width, etc """ + import datetime + import osxphotos + + photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB) + photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]]) + assert len(photos) == 1 + p = photos[0] + assert p.keywords == ["wedding"] + assert p.original_filename == "wedding.jpg" + assert p.filename == "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51.jpeg" + assert p.date == datetime.datetime( + 2019, + 4, + 15, + 14, + 40, + 24, + 86000, + datetime.timezone(datetime.timedelta(seconds=-14400)), + ) + assert p.description == "Bride Wedding day" + assert p.title is None + assert sorted(p.albums) == ["AlbumInFolder", "I have a deleted twin"] + assert p.persons == ["Maria"] + assert p.path.endswith( + "tests/Test-10.15.5.photoslibrary/originals/E/E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51.jpeg" + ) + assert not p.ismissing + assert p.hasadjustments + assert p.height == 1325 + assert p.width == 1526 + assert p.original_height == 1367 + assert p.original_width == 2048 + assert p.orientation == 1 + assert p.original_orientation == 1 + assert p.original_filesize == 460483 + + def test_missing(): import osxphotos diff --git a/tests/test_mojave_10_14_6.py b/tests/test_mojave_10_14_6.py index 90da828e..ea397937 100644 --- a/tests/test_mojave_10_14_6.py +++ b/tests/test_mojave_10_14_6.py @@ -44,6 +44,7 @@ UUID_DICT = { "date_invalid": "YZFCPY24TUySvpu7owiqxA", "intrash": "3tljdX43R8+k6peNHVrJNQ", "not_intrash": "6bxcNnzRQKGnK4uPrCJ9UQ", + "has_adjustments": "6bxcNnzRQKGnK4uPrCJ9UQ", } PHOTOS_DB_LEN = 8 @@ -161,6 +162,44 @@ def test_attributes(): assert p.ismissing == False +def test_attributes_2(): + """ Test attributes including height, width, etc """ + import datetime + import osxphotos + + photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB) + photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]]) + assert len(photos) == 1 + p = photos[0] + assert p.keywords == ["wedding"] + assert p.original_filename == "wedding.jpg" + assert p.filename == "wedding.jpg" + assert p.date == datetime.datetime( + 2019, + 4, + 15, + 14, + 40, + 24, + 86000, + datetime.timezone(datetime.timedelta(seconds=-14400)), + ) + assert p.description == "Bride Wedding day" + assert p.title is None + assert sorted(p.albums) == [] + assert p.persons == ["Maria"] + assert p.path.endswith("Masters/2019/07/27/20190727-131650/wedding.jpg") + assert not p.ismissing + assert p.hasadjustments + assert p.height == 1325 + assert p.width == 1526 + assert p.original_height == 1367 + assert p.original_width == 2048 + assert p.orientation == 1 + assert p.original_orientation == 1 + assert p.original_filesize == 460483 + + def test_missing(): import osxphotos