Added incloud and iscloudasset to PhotoInfo (Photos 5)
This commit is contained in:
@@ -47,6 +47,8 @@
|
||||
- [`shared`](#shared)
|
||||
- [`isphoto`](#isphoto)
|
||||
- [`ismovie`](#ismovie)
|
||||
- [`iscloudasset`](#iscloudasset)
|
||||
- [`incloud`](#incloud)
|
||||
- [`uti`](#uti)
|
||||
- [`burst`](#burst)
|
||||
- [`burst_photos`](#burst_photos)
|
||||
@@ -597,6 +599,12 @@ Returns True if type is photo/still image, otherwise False
|
||||
#### `ismovie`
|
||||
Returns True if type is movie/video, otherwise False
|
||||
|
||||
#### `iscloudasset`
|
||||
Returns True if photo is a cloud asset, that is, it is in a library synched to iCloud. See also [incloud](#incloud)
|
||||
|
||||
#### `incloud`
|
||||
Returns True if photo is a [cloud asset](#iscloudasset) and is synched to iCloud otherwise False if photo is a cloud asset and not yet synched to iCloud. Returns None if photo is not a cloud asset.
|
||||
|
||||
#### `uti`
|
||||
Returns Uniform Type Identifier (UTI) for the image, for example: 'public.jpeg' or 'com.apple.quicktime-movie'
|
||||
|
||||
|
||||
@@ -312,6 +312,21 @@ class PhotoInfo:
|
||||
"""
|
||||
return True if self._info["type"] == _PHOTO_TYPE else False
|
||||
|
||||
@property
|
||||
def incloud(self):
|
||||
""" Returns True if photo is cloud asset and is synched to cloud
|
||||
False if photo is cloud asset and not yet synched to cloud
|
||||
None if photo is not cloud asset
|
||||
"""
|
||||
return self._info["incloud"]
|
||||
|
||||
@property
|
||||
def iscloudasset(self):
|
||||
""" Returns True if photo is a cloud asset (in an iCloud library),
|
||||
otherwise False
|
||||
"""
|
||||
return True if self._info["cloudAssetGUID"] is not None else False
|
||||
|
||||
@property
|
||||
def burst(self):
|
||||
""" Returns True if photo is part of a Burst photo set, otherwise False """
|
||||
|
||||
@@ -628,6 +628,12 @@ class PhotosDB:
|
||||
|
||||
# TODO: Handle selfies (front facing camera, RKVersion.selfPortrait == 1)
|
||||
# self._dbphotos[uuid]["selfie"] = True if row[27] == 1 else False
|
||||
self._dbphotos[uuid]["selfie"] = None
|
||||
|
||||
# Init cloud details that will be filled in later
|
||||
self._dbphotos[uuid]["cloudAssetGUID"] = None
|
||||
self._dbphotos[uuid]["cloudLocalState"] = None # will be initialized later if is cloud asset
|
||||
self._dbphotos[uuid]["incloud"] = None # will be initialized later if is cloud asset
|
||||
|
||||
# get details needed to find path of the edited photos
|
||||
c.execute(
|
||||
@@ -929,7 +935,8 @@ class PhotosDB:
|
||||
ZGENERICASSET.ZAVALANCHEPICKTYPE,
|
||||
ZGENERICASSET.ZKINDSUBTYPE,
|
||||
ZGENERICASSET.ZCUSTOMRENDEREDVALUE,
|
||||
ZADDITIONALASSETATTRIBUTES.ZCAMERACAPTUREDEVICE
|
||||
ZADDITIONALASSETATTRIBUTES.ZCAMERACAPTUREDEVICE,
|
||||
ZGENERICASSET.ZCLOUDASSETGUID
|
||||
FROM ZGENERICASSET
|
||||
JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = ZGENERICASSET.Z_PK
|
||||
WHERE ZGENERICASSET.ZTRASHEDSTATE = 0
|
||||
@@ -960,6 +967,9 @@ class PhotosDB:
|
||||
# 21 ZGENERICASSET.ZKINDSUBTYPE -- determine if live photos, etc
|
||||
# 22 ZGENERICASSET.ZCUSTOMRENDEREDVALUE -- determine if HDR photo
|
||||
# 23 ZADDITIONALASSETATTRIBUTES.ZCAMERACAPTUREDEVICE -- 1 if selfie (front facing camera)
|
||||
# 25 ZGENERICASSET.ZCLOUDASSETGUID -- not null if asset is cloud asset
|
||||
# (e.g. user has "iCloud Photos" checked in Photos preferences)
|
||||
|
||||
|
||||
|
||||
for row in c:
|
||||
@@ -1070,6 +1080,11 @@ class PhotosDB:
|
||||
# Handle selfies (front facing camera, ZCAMERACAPTUREDEVICE=1)
|
||||
info["selfie"] = True if row[23] == 1 else False
|
||||
|
||||
# Determine if photo is part of cloud library (ZGENERICASSET.ZCLOUDASSETGUID not NULL)
|
||||
info["cloudAssetGUID"] = row[24]
|
||||
info["cloudLocalState"] = None # will be initialized later if is cloud asset
|
||||
info["incloud"] = None # will be initialized later if is cloud asset
|
||||
|
||||
self._dbphotos[uuid] = info
|
||||
|
||||
# # if row[19] is not None and ((row[20] == 2) or (row[20] == 4)):
|
||||
@@ -1131,9 +1146,6 @@ class PhotosDB:
|
||||
# Get info on remote/local availability for photos in shared albums
|
||||
# Shared photos have a null fingerprint (and some other photos do too)
|
||||
# TODO: There may be a bug here, perhaps ZDATASTORESUBTYPE should be 1 --> it's the longest ZDATALENGTH (is this the original)
|
||||
# Also, doesn't seem to be entirely accurate for PNGs (screenshots mostly)
|
||||
# for PNGs, JPEG render seems to be used unless edited or exported
|
||||
# see for example ./resources/renders/F/F2FF9B89-FB6F-4853-942B-9F8BEE8DFFA1_1_201_a.jpeg
|
||||
c.execute(
|
||||
""" SELECT
|
||||
ZGENERICASSET.ZUUID,
|
||||
@@ -1195,8 +1207,19 @@ class PhotosDB:
|
||||
# f"{uuid} isMissing changed: {old} {self._dbphotos[uuid]['isMissing']}"
|
||||
# )
|
||||
|
||||
if _debug():
|
||||
logging.debug(pformat(self._dbphotos))
|
||||
# get information about cloud sync state
|
||||
c.execute(
|
||||
""" SELECT
|
||||
ZGENERICASSET.ZUUID,
|
||||
ZCLOUDMASTER.ZCLOUDLOCALSTATE
|
||||
FROM ZCLOUDMASTER, ZGENERICASSET
|
||||
WHERE ZGENERICASSET.ZMASTER = ZCLOUDMASTER.Z_PK """
|
||||
)
|
||||
for row in c:
|
||||
uuid = row[0]
|
||||
if uuid in self._dbphotos:
|
||||
self._dbphotos[uuid]["cloudLocalState"] = row[1]
|
||||
self._dbphotos[uuid]["incloud"] = True if row[1] == 3 else False
|
||||
|
||||
# add faces and keywords to photo data
|
||||
for uuid in self._dbphotos:
|
||||
@@ -1226,6 +1249,7 @@ class PhotosDB:
|
||||
conn.close()
|
||||
self._cleanup_tmp_files()
|
||||
|
||||
# done processing, dump debug data if requested
|
||||
if _debug():
|
||||
logging.debug("Faces:")
|
||||
logging.debug(pformat(self._dbfaces_uuid))
|
||||
@@ -1254,7 +1278,6 @@ class PhotosDB:
|
||||
logging.debug("Burst Photos:")
|
||||
logging.debug(pformat(self._dbphotos_burst))
|
||||
|
||||
# TODO: fix default values to None instead of []
|
||||
def photos(
|
||||
self,
|
||||
keywords=None,
|
||||
|
||||
60
tests/test_incloud.py
Normal file
60
tests/test_incloud.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# Test cloud photos
|
||||
|
||||
import pytest
|
||||
|
||||
PHOTOS_DB_CLOUD = "./tests/Test-Cloud-10.15.1.photoslibrary/database/photos.db"
|
||||
PHOTOS_DB_NOT_CLOUD = "./tests/Test-10.15.1.photoslibrary/database/photos.db"
|
||||
|
||||
UUID_DICT = {
|
||||
"incloud": "37210110-E940-4227-92D3-45C40F68EB0A",
|
||||
"not_incloud": "E5BC411D-30EE-44D3-84C0-54760A10579D",
|
||||
"cloudasset": "D11D25FF-5F31-47D2-ABA9-58418878DC15",
|
||||
"not_cloudasset": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
|
||||
}
|
||||
|
||||
|
||||
def test_incloud():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["incloud"]])
|
||||
|
||||
assert photos[0].incloud
|
||||
|
||||
|
||||
def test_not_incloud():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
||||
|
||||
assert not photos[0].incloud
|
||||
|
||||
|
||||
def test_cloudasset_1():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["cloudasset"]])
|
||||
|
||||
assert photos[0].iscloudasset
|
||||
|
||||
|
||||
def test_cloudasset_2():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_CLOUD)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["not_incloud"]])
|
||||
|
||||
# not_incloud is still a cloud asset
|
||||
assert photos[0].iscloudasset
|
||||
|
||||
|
||||
def test_cloudasset_3():
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(PHOTOS_DB_NOT_CLOUD)
|
||||
photos = photosdb.photos(uuid=[UUID_DICT["not_cloudasset"]])
|
||||
|
||||
assert not photos[0].iscloudasset
|
||||
|
||||
Reference in New Issue
Block a user