Added incloud and iscloudasset to PhotoInfo (Photos 5)
This commit is contained in:
@@ -47,6 +47,8 @@
|
|||||||
- [`shared`](#shared)
|
- [`shared`](#shared)
|
||||||
- [`isphoto`](#isphoto)
|
- [`isphoto`](#isphoto)
|
||||||
- [`ismovie`](#ismovie)
|
- [`ismovie`](#ismovie)
|
||||||
|
- [`iscloudasset`](#iscloudasset)
|
||||||
|
- [`incloud`](#incloud)
|
||||||
- [`uti`](#uti)
|
- [`uti`](#uti)
|
||||||
- [`burst`](#burst)
|
- [`burst`](#burst)
|
||||||
- [`burst_photos`](#burst_photos)
|
- [`burst_photos`](#burst_photos)
|
||||||
@@ -597,6 +599,12 @@ Returns True if type is photo/still image, otherwise False
|
|||||||
#### `ismovie`
|
#### `ismovie`
|
||||||
Returns True if type is movie/video, otherwise False
|
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`
|
#### `uti`
|
||||||
Returns Uniform Type Identifier (UTI) for the image, for example: 'public.jpeg' or 'com.apple.quicktime-movie'
|
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
|
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
|
@property
|
||||||
def burst(self):
|
def burst(self):
|
||||||
""" Returns True if photo is part of a Burst photo set, otherwise False """
|
""" 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)
|
# TODO: Handle selfies (front facing camera, RKVersion.selfPortrait == 1)
|
||||||
# self._dbphotos[uuid]["selfie"] = True if row[27] == 1 else False
|
# 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
|
# get details needed to find path of the edited photos
|
||||||
c.execute(
|
c.execute(
|
||||||
@@ -929,7 +935,8 @@ class PhotosDB:
|
|||||||
ZGENERICASSET.ZAVALANCHEPICKTYPE,
|
ZGENERICASSET.ZAVALANCHEPICKTYPE,
|
||||||
ZGENERICASSET.ZKINDSUBTYPE,
|
ZGENERICASSET.ZKINDSUBTYPE,
|
||||||
ZGENERICASSET.ZCUSTOMRENDEREDVALUE,
|
ZGENERICASSET.ZCUSTOMRENDEREDVALUE,
|
||||||
ZADDITIONALASSETATTRIBUTES.ZCAMERACAPTUREDEVICE
|
ZADDITIONALASSETATTRIBUTES.ZCAMERACAPTUREDEVICE,
|
||||||
|
ZGENERICASSET.ZCLOUDASSETGUID
|
||||||
FROM ZGENERICASSET
|
FROM ZGENERICASSET
|
||||||
JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = ZGENERICASSET.Z_PK
|
JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = ZGENERICASSET.Z_PK
|
||||||
WHERE ZGENERICASSET.ZTRASHEDSTATE = 0
|
WHERE ZGENERICASSET.ZTRASHEDSTATE = 0
|
||||||
@@ -960,6 +967,9 @@ class PhotosDB:
|
|||||||
# 21 ZGENERICASSET.ZKINDSUBTYPE -- determine if live photos, etc
|
# 21 ZGENERICASSET.ZKINDSUBTYPE -- determine if live photos, etc
|
||||||
# 22 ZGENERICASSET.ZCUSTOMRENDEREDVALUE -- determine if HDR photo
|
# 22 ZGENERICASSET.ZCUSTOMRENDEREDVALUE -- determine if HDR photo
|
||||||
# 23 ZADDITIONALASSETATTRIBUTES.ZCAMERACAPTUREDEVICE -- 1 if selfie (front facing camera)
|
# 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:
|
for row in c:
|
||||||
@@ -1070,6 +1080,11 @@ class PhotosDB:
|
|||||||
# Handle selfies (front facing camera, ZCAMERACAPTUREDEVICE=1)
|
# Handle selfies (front facing camera, ZCAMERACAPTUREDEVICE=1)
|
||||||
info["selfie"] = True if row[23] == 1 else False
|
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
|
self._dbphotos[uuid] = info
|
||||||
|
|
||||||
# # if row[19] is not None and ((row[20] == 2) or (row[20] == 4)):
|
# # 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
|
# Get info on remote/local availability for photos in shared albums
|
||||||
# Shared photos have a null fingerprint (and some other photos do too)
|
# 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)
|
# 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(
|
c.execute(
|
||||||
""" SELECT
|
""" SELECT
|
||||||
ZGENERICASSET.ZUUID,
|
ZGENERICASSET.ZUUID,
|
||||||
@@ -1195,8 +1207,19 @@ class PhotosDB:
|
|||||||
# f"{uuid} isMissing changed: {old} {self._dbphotos[uuid]['isMissing']}"
|
# f"{uuid} isMissing changed: {old} {self._dbphotos[uuid]['isMissing']}"
|
||||||
# )
|
# )
|
||||||
|
|
||||||
if _debug():
|
# get information about cloud sync state
|
||||||
logging.debug(pformat(self._dbphotos))
|
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
|
# add faces and keywords to photo data
|
||||||
for uuid in self._dbphotos:
|
for uuid in self._dbphotos:
|
||||||
@@ -1226,6 +1249,7 @@ class PhotosDB:
|
|||||||
conn.close()
|
conn.close()
|
||||||
self._cleanup_tmp_files()
|
self._cleanup_tmp_files()
|
||||||
|
|
||||||
|
# done processing, dump debug data if requested
|
||||||
if _debug():
|
if _debug():
|
||||||
logging.debug("Faces:")
|
logging.debug("Faces:")
|
||||||
logging.debug(pformat(self._dbfaces_uuid))
|
logging.debug(pformat(self._dbfaces_uuid))
|
||||||
@@ -1254,7 +1278,6 @@ class PhotosDB:
|
|||||||
logging.debug("Burst Photos:")
|
logging.debug("Burst Photos:")
|
||||||
logging.debug(pformat(self._dbphotos_burst))
|
logging.debug(pformat(self._dbphotos_burst))
|
||||||
|
|
||||||
# TODO: fix default values to None instead of []
|
|
||||||
def photos(
|
def photos(
|
||||||
self,
|
self,
|
||||||
keywords=None,
|
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