diff --git a/osxphotos/_constants.py b/osxphotos/_constants.py
index 11059831..714a32aa 100644
--- a/osxphotos/_constants.py
+++ b/osxphotos/_constants.py
@@ -17,8 +17,8 @@ _TESTED_DB_VERSIONS = ["6000", "4025", "4016", "3301", "2622"]
_PHOTOS_3_VERSION = "3301"
# versions 5.0 and later have a different database structure
-_PHOTOS_4_VERSION = "4025" # latest Mojove version on 10.14.6
-_PHOTOS_5_VERSION = "6000" # seems to be current on 10.15.1 through 10.14.4
+_PHOTOS_4_VERSION = "4025" # latest Mojove version on 10.14.6
+_PHOTOS_5_VERSION = "6000" # seems to be current on 10.15.1 through 10.15.4
# which major version operating systems have been tested
_TESTED_OS_VERSIONS = ["12", "13", "14", "15"]
@@ -47,3 +47,7 @@ _PHOTOS_5_ALBUM_KIND = 2 # normal user album
_PHOTOS_5_SHARED_ALBUM_KIND = 1505 # shared album
_PHOTOS_5_FOLDER_KIND = 4000 # user folder
_PHOTOS_5_ROOT_FOLDER_KIND = 3999 # root folder
+
+_PHOTOS_4_ALBUM_KIND = 3 # RKAlbum.albumSubclass
+_PHOTOS_4_TOP_LEVEL_ALBUM = "TopLevelAlbums"
+_PHOTOS_4_ROOT_FOLDER = "LibraryFolder"
diff --git a/osxphotos/_version.py b/osxphotos/_version.py
index f394d84c..b4fa57e0 100644
--- a/osxphotos/_version.py
+++ b/osxphotos/_version.py
@@ -1,3 +1,3 @@
""" version info """
-__version__ = "0.28.1"
+__version__ = "0.28.2"
diff --git a/osxphotos/albuminfo.py b/osxphotos/albuminfo.py
index 9c545b99..4b1e4908 100644
--- a/osxphotos/albuminfo.py
+++ b/osxphotos/albuminfo.py
@@ -12,7 +12,13 @@ PhotosDB.folders() returns a list of FolderInfo objects
import logging
-from ._constants import _PHOTOS_5_ALBUM_KIND, _PHOTOS_5_FOLDER_KIND, _PHOTOS_5_VERSION
+from ._constants import (
+ _PHOTOS_4_ALBUM_KIND,
+ _PHOTOS_4_TOP_LEVEL_ALBUM,
+ _PHOTOS_4_VERSION,
+ _PHOTOS_5_ALBUM_KIND,
+ _PHOTOS_5_FOLDER_KIND,
+)
class AlbumInfo:
@@ -53,14 +59,13 @@ class AlbumInfo:
["Top level folder", "sub folder 1", "sub folder 2", ...]
returns empty list if album is not in any folders """
- if self._db._db_version < _PHOTOS_5_VERSION:
- logging.warning("Folders not yet implemented for this DB version")
- return []
-
try:
return self._folder_names
except AttributeError:
- self._folder_names = self._db._album_folder_hierarchy_list(self._uuid)
+ if self._db._db_version <= _PHOTOS_4_VERSION:
+ self._folder_names = self._db._album_folder_hierarchy_list(self._uuid)
+ else:
+ self._folder_names = self._db._album_folder_hierarchy_list(self._uuid)
return self._folder_names
@property
@@ -70,10 +75,6 @@ class AlbumInfo:
["Top level folder", "sub folder 1", "sub folder 2", ...]
returns empty list if album is not in any folders """
- if self._db._db_version < _PHOTOS_5_VERSION:
- logging.warning("Folders not yet implemented for this DB version")
- return []
-
try:
return self._folders
except AttributeError:
@@ -83,19 +84,23 @@ class AlbumInfo:
@property
def parent(self):
""" returns FolderInfo object for parent folder or None if no parent (e.g. top-level album) """
- if self._db._db_version < _PHOTOS_5_VERSION:
- logging.warning("Folders not yet implemented for this DB version")
- return None
-
try:
return self._parent
except AttributeError:
- parent_pk = self._db._dbalbum_details[self._uuid]["parentfolder"]
- self._parent = (
- FolderInfo(db=self._db, uuid=self._db._dbalbums_pk[parent_pk])
- if parent_pk != self._db._folder_root_pk
- else None
- )
+ if self._db._db_version <= _PHOTOS_4_VERSION:
+ parent_uuid = self._db._dbalbum_details[self._uuid]["folderUuid"]
+ self._parent = (
+ FolderInfo(db=self._db, uuid=parent_uuid)
+ if parent_uuid != _PHOTOS_4_TOP_LEVEL_ALBUM
+ else None
+ )
+ else:
+ parent_pk = self._db._dbalbum_details[self._uuid]["parentfolder"]
+ self._parent = (
+ FolderInfo(db=self._db, uuid=self._db._dbalbums_pk[parent_pk])
+ if parent_pk != self._db._folder_root_pk
+ else None
+ )
return self._parent
def __len__(self):
@@ -112,8 +117,12 @@ class FolderInfo:
def __init__(self, db=None, uuid=None):
self._uuid = uuid
self._db = db
- self._pk = self._db._dbalbum_details[uuid]["pk"]
- self._title = self._db._dbalbum_details[uuid]["title"]
+ if self._db._db_version <= _PHOTOS_4_VERSION:
+ self._pk = None
+ self._title = self._db._dbfolder_details[uuid]["name"]
+ else:
+ self._pk = self._db._dbalbum_details[uuid]["pk"]
+ self._title = self._db._dbalbum_details[uuid]["title"]
@property
def title(self):
@@ -131,13 +140,22 @@ class FolderInfo:
try:
return self._albums
except AttributeError:
- albums = [
- AlbumInfo(db=self._db, uuid=album)
- for album, detail in self._db._dbalbum_details.items()
- if not detail["intrash"]
- and detail["kind"] == _PHOTOS_5_ALBUM_KIND
- and detail["parentfolder"] == self._pk
- ]
+ if self._db._db_version <= _PHOTOS_4_VERSION:
+ albums = [
+ AlbumInfo(db=self._db, uuid=album)
+ for album, detail in self._db._dbalbum_details.items()
+ if not detail["intrash"]
+ and detail["albumSubclass"] == _PHOTOS_4_ALBUM_KIND
+ and detail["folderUuid"] == self._uuid
+ ]
+ else:
+ albums = [
+ AlbumInfo(db=self._db, uuid=album)
+ for album, detail in self._db._dbalbum_details.items()
+ if not detail["intrash"]
+ and detail["kind"] == _PHOTOS_5_ALBUM_KIND
+ and detail["parentfolder"] == self._pk
+ ]
self._albums = albums
return self._albums
@@ -147,12 +165,20 @@ class FolderInfo:
try:
return self._parent
except AttributeError:
- parent_pk = self._db._dbalbum_details[self._uuid]["parentfolder"]
- self._parent = (
- FolderInfo(db=self._db, uuid=self._db._dbalbums_pk[parent_pk])
- if parent_pk != self._db._folder_root_pk
- else None
- )
+ if self._db._db_version <= _PHOTOS_4_VERSION:
+ parent_uuid = self._db._dbfolder_details[self._uuid]["parentFolderUuid"]
+ self._parent = (
+ FolderInfo(db=self._db, uuid=parent_uuid)
+ if parent_uuid != _PHOTOS_4_TOP_LEVEL_ALBUM
+ else None
+ )
+ else:
+ parent_pk = self._db._dbalbum_details[self._uuid]["parentfolder"]
+ self._parent = (
+ FolderInfo(db=self._db, uuid=self._db._dbalbums_pk[parent_pk])
+ if parent_pk != self._db._folder_root_pk
+ else None
+ )
return self._parent
@property
@@ -161,13 +187,22 @@ class FolderInfo:
try:
return self._folders
except AttributeError:
- folders = [
- FolderInfo(db=self._db, uuid=album)
- for album, detail in self._db._dbalbum_details.items()
- if not detail["intrash"]
- and detail["kind"] == _PHOTOS_5_FOLDER_KIND
- and detail["parentfolder"] == self._pk
- ]
+ if self._db._db_version <= _PHOTOS_4_VERSION:
+ folders = [
+ FolderInfo(db=self._db, uuid=folder)
+ for folder, detail in self._db._dbfolder_details.items()
+ if not detail["intrash"]
+ and not detail["isMagic"]
+ and detail["parentFolderUuid"] == self._uuid
+ ]
+ else:
+ folders = [
+ FolderInfo(db=self._db, uuid=album)
+ for album, detail in self._db._dbalbum_details.items()
+ if not detail["intrash"]
+ and detail["kind"] == _PHOTOS_5_FOLDER_KIND
+ and detail["parentfolder"] == self._pk
+ ]
self._folders = folders
return self._folders
diff --git a/osxphotos/photoinfo.py b/osxphotos/photoinfo.py
index b2c7438c..fc238aa5 100644
--- a/osxphotos/photoinfo.py
+++ b/osxphotos/photoinfo.py
@@ -21,8 +21,8 @@ from mako.template import Template
from ._constants import (
_MOVIE_TYPE,
_PHOTO_TYPE,
+ _PHOTOS_4_VERSION,
_PHOTOS_5_SHARED_PHOTO_PATH,
- _PHOTOS_5_VERSION,
_TEMPLATE_DIR,
_XMP_TEMPLATE_NAME,
)
@@ -98,7 +98,7 @@ class PhotoInfo:
if self._info["isMissing"] == 1:
return photopath # path would be meaningless until downloaded
- if self._db._db_version < _PHOTOS_5_VERSION:
+ if self._db._db_version <= _PHOTOS_4_VERSION:
vol = self._info["volume"]
if vol is not None:
photopath = os.path.join("/Volumes", vol, self._info["imagePath"])
@@ -137,7 +137,7 @@ class PhotoInfo:
photopath = None
- if self._db._db_version < _PHOTOS_5_VERSION:
+ if self._db._db_version <= _PHOTOS_4_VERSION:
if self._info["hasAdjustments"]:
edit_id = self._info["edit_resource_id"]
if edit_id is not None:
@@ -269,7 +269,7 @@ class PhotoInfo:
# )
# return photopath
- if self._db._db_version < _PHOTOS_5_VERSION:
+ if self._db._db_version <= _PHOTOS_4_VERSION:
vol = self._info["raw_info"]["volume"]
if vol is not None:
photopath = os.path.join(
@@ -400,7 +400,7 @@ class PhotoInfo:
def shared(self):
""" returns True if photos is in a shared iCloud album otherwise false
Only valid on Photos 5; returns None on older versions """
- if self._db._db_version >= _PHOTOS_5_VERSION:
+ if self._db._db_version > _PHOTOS_4_VERSION:
return self._info["shared"]
else:
return None
@@ -445,7 +445,7 @@ class PhotoInfo:
""" Returns True if photo is a cloud asset (in an iCloud library),
otherwise False
"""
- if self._db._db_version < _PHOTOS_5_VERSION:
+ if self._db._db_version <= _PHOTOS_4_VERSION:
return (
True
if self._info["cloudLibraryState"] is not None
@@ -488,7 +488,7 @@ class PhotoInfo:
If photo is missing, returns None """
photopath = None
- if self._db._db_version < _PHOTOS_5_VERSION:
+ if self._db._db_version <= _PHOTOS_4_VERSION:
if self.live_photo and not self.ismissing:
live_model_id = self._info["live_model_id"]
if live_model_id == None:
@@ -579,7 +579,7 @@ class PhotoInfo:
# implementation note: doesn't create the PlaceInfo object until requested
# then memoizes the object in self._place to avoid recreating the object
- if self._db._db_version < _PHOTOS_5_VERSION:
+ if self._db._db_version <= _PHOTOS_4_VERSION:
try:
return self._place # pylint: disable=access-member-before-definition
except AttributeError:
diff --git a/osxphotos/photosdb.py b/osxphotos/photosdb.py
index 1f1f465c..86d6b812 100644
--- a/osxphotos/photosdb.py
+++ b/osxphotos/photosdb.py
@@ -24,6 +24,8 @@ from ._constants import (
_TESTED_DB_VERSIONS,
_TESTED_OS_VERSIONS,
_UNKNOWN_PERSON,
+ _PHOTOS_4_ALBUM_KIND,
+ _PHOTOS_4_TOP_LEVEL_ALBUM,
_PHOTOS_5_ROOT_FOLDER_KIND,
_PHOTOS_5_FOLDER_KIND,
_PHOTOS_5_ALBUM_KIND,
@@ -173,6 +175,9 @@ class PhotosDB:
# e.g. {'AA4145F5-098C-496E-9197-B7584958FF9B': {'99D24D3E-59E7-465F-B386-A48A94B00BC1': {'F2246D82-1A12-4994-9654-3DC6FE38A7A8': None}}, }
self._dbalbum_folders = {}
+ # Dict with information about folders
+ self._dbfolder_details = {}
+
# Will hold the primary key of root folder
self._folder_root_pk = None
@@ -312,7 +317,7 @@ class PhotosDB:
valid only on Photos 5; on Photos <= 4, prints warning and returns empty dict """
# if _dbalbum_details[key]["cloudownerhashedpersonid"] is not None, then it's a shared album
- if self._db_version < _PHOTOS_5_VERSION:
+ if self._db_version <= _PHOTOS_4_VERSION:
logging.warning(
f"albums_shared not implemented for Photos versions < {_PHOTOS_5_VERSION}"
)
@@ -348,33 +353,43 @@ class PhotosDB:
@property
def folder_info(self):
""" return list FolderInfo objects representing top-level folders in the photos database """
- if self._db_version < _PHOTOS_5_VERSION:
- logging.warning("Folders not yet implemented for this DB version")
- return []
-
- folders = [
- FolderInfo(db=self, uuid=album)
- for album, detail in self._dbalbum_details.items()
- if not detail["intrash"]
- and detail["kind"] == _PHOTOS_5_FOLDER_KIND
- and detail["parentfolder"] == self._folder_root_pk
- ]
+ if self._db_version <= _PHOTOS_4_VERSION:
+ folders = [
+ FolderInfo(db=self, uuid=folder)
+ for folder, detail in self._dbfolder_details.items()
+ if not detail["intrash"]
+ and not detail["isMagic"]
+ and detail["parentFolderUuid"] == _PHOTOS_4_TOP_LEVEL_ALBUM
+ ]
+ else:
+ folders = [
+ FolderInfo(db=self, uuid=album)
+ for album, detail in self._dbalbum_details.items()
+ if not detail["intrash"]
+ and detail["kind"] == _PHOTOS_5_FOLDER_KIND
+ and detail["parentfolder"] == self._folder_root_pk
+ ]
return folders
@property
def folders(self):
""" return list of top-level folder names in the photos database """
- if self._db_version < _PHOTOS_5_VERSION:
- logging.warning("Folders not yet implemented for this DB version")
- return []
-
- folder_names = [
- detail["title"]
- for detail in self._dbalbum_details.values()
- if not detail["intrash"]
- and detail["kind"] == _PHOTOS_5_FOLDER_KIND
- and detail["parentfolder"] == self._folder_root_pk
- ]
+ if self._db_version <= _PHOTOS_4_VERSION:
+ folder_names = [
+ folder["name"]
+ for folder in self._dbfolder_details.values()
+ if not folder["intrash"]
+ and not folder["isMagic"]
+ and folder["parentFolderUuid"] == _PHOTOS_4_TOP_LEVEL_ALBUM
+ ]
+ else:
+ folder_names = [
+ detail["title"]
+ for detail in self._dbalbum_details.values()
+ if not detail["intrash"]
+ and detail["kind"] == _PHOTOS_5_FOLDER_KIND
+ and detail["parentfolder"] == self._folder_root_pk
+ ]
return folder_names
@property
@@ -395,7 +410,7 @@ class PhotosDB:
only valid for Photos 5; on Photos <= 4, prints warning and returns empty list """
# if _dbalbum_details[key]["cloudownerhashedpersonid"] is not None, then it's a shared album
- if self._db_version < _PHOTOS_5_VERSION:
+ if self._db_version <= _PHOTOS_4_VERSION:
logging.warning(
f"albums_shared not implemented for Photos versions < {_PHOTOS_5_VERSION}"
)
@@ -434,7 +449,7 @@ class PhotosDB:
# if _dbalbum_details[key]["cloudownerhashedpersonid"] is not None, then it's a shared album
- if self._db_version < _PHOTOS_5_VERSION:
+ if self._db_version <= _PHOTOS_4_VERSION:
logging.warning(
f"album_names_shared not implemented for Photos versions < {_PHOTOS_5_VERSION}"
)
@@ -538,7 +553,10 @@ class PhotosDB:
# Get info on albums
c.execute(
- """ select RKAlbum.uuid, RKVersion.uuid from RKAlbum, RKVersion, RKAlbumVersion
+ """ select
+ RKAlbum.uuid,
+ RKVersion.uuid
+ from RKAlbum, RKVersion, RKAlbumVersion
where RKAlbum.modelID = RKAlbumVersion.albumId and
RKAlbumVersion.versionID = RKVersion.modelId
and RKVersion.isInTrash = 0 """
@@ -554,17 +572,31 @@ class PhotosDB:
# now get additional details about albums
c.execute(
- "SELECT "
- "uuid, " # 0
- "name, " # 1
- "cloudLibraryState, " # 2
- "cloudIdentifier, " # 3
- "isInTrash " # 4
- "FROM RKAlbum "
+ """ SELECT
+ uuid,
+ name,
+ cloudLibraryState,
+ cloudIdentifier,
+ isInTrash,
+ folderUuid,
+ albumType,
+ albumSubclass
+ FROM RKAlbum """
)
+ # Order of results
+ # 0: uuid
+ # 1: name
+ # 2: cloudLibraryState
+ # 3: cloudIdentifier
+ # 4: isInTrash
+ # 5: folderUuid
+ # 6: albumType
+ # 7: albumSubclass -- if 3, normal user album
+
for album in c:
self._dbalbum_details[album[0]] = {
+ "_uuid": album[0],
"title": album[1],
"cloudlibrarystate": album[2],
"cloudidentifier": album[3],
@@ -573,13 +605,71 @@ class PhotosDB:
"cloudownerfirstname": None, # Photos 5
"cloudownderlastname": None, # Photos 5
"cloudownerhashedpersonid": None, # Photos 5
+ "folderUuid": album[5],
+ "albumType": album[6],
+ "albumSubclass": album[7],
}
+ # get details about folders
+ c.execute(
+ """ SELECT
+ uuid,
+ modelId,
+ name,
+ isMagic,
+ isInTrash,
+ folderType,
+ parentFolderUuid,
+ folderPath
+ FROM RKFolder """
+ )
+
+ # Order of results
+ # 0 uuid,
+ # 1 modelId,
+ # 2 name,
+ # 3 isMagic,
+ # 4 isInTrash,
+ # 5 folderType,
+ # 6 parentFolderUuid,
+ # 7 folderPath
+
+ for row in c:
+ uuid = row[0]
+ self._dbfolder_details[uuid] = {
+ "_uuid": row[0],
+ "modelId": row[1],
+ "name": row[2],
+ "isMagic": row[3],
+ "intrash": row[4],
+ "folderType": row[5],
+ "parentFolderUuid": row[6],
+ "folderPath": row[7],
+ }
+
+ # build _dbalbum_folders in form uuid: [parent uuid] to be consistent with _process_database5
+ for album, details in self._dbalbum_details.items():
+ # album can be in a single folder
+ parent = details["folderUuid"]
+ self._dbalbum_parent_folders[album] = [parent]
+
+ # build folder hierarchy
+ for album, details in self._dbalbum_details.items():
+ parent_folder = details["folderUuid"]
+ if parent_folder != _PHOTOS_4_TOP_LEVEL_ALBUM:
+ # logging.warning(f"album = {details['title']}, parent = {parent_folder}")
+ folder_hierarchy = self._build_album_folder_hierarchy_4(parent_folder)
+ self._dbalbum_folders[album] = folder_hierarchy
+ else:
+ self._dbalbum_folders[album] = {}
+
if _debug():
logging.debug(f"Finished walking through albums")
logging.debug(pformat(self._dbalbums_album))
logging.debug(pformat(self._dbalbums_uuid))
logging.debug(pformat(self._dbalbum_details))
+ logging.debug(pformat(self._dbalbum_folders))
+ logging.debug(pformat(self._dbfolder_details))
# Get info on keywords
c.execute(
@@ -1122,6 +1212,30 @@ class PhotosDB:
logging.debug("Burst Photos (dbphotos_burst:")
logging.debug(pformat(self._dbphotos_burst))
+ def _build_album_folder_hierarchy_4(self, uuid, folders=None):
+ """ recursively build folder/album hierarchy
+ uuid: uuid of the album/folder being processed
+ folders: dict holding the folder hierarchy """
+
+ parent_uuid = self._dbfolder_details[uuid]["parentFolderUuid"]
+
+ # logging.warning(f"uuid = {uuid}, parent = {parent_uuid}, folders = {folders}")
+
+ if parent_uuid is None:
+ return folders
+
+ if parent_uuid == _PHOTOS_4_TOP_LEVEL_ALBUM:
+ # at top of hierarchy, we're done
+ return folders
+
+ # recurse to keep building
+ if not folders:
+ # first time building
+ folders = {uuid: None}
+ folders = {parent_uuid: folders}
+ folders = self._build_album_folder_hierarchy_4(parent_uuid, folders=folders)
+ return folders
+
def _process_database5(self):
""" process the Photos database to extract info """
""" works on Photos version >= 5.0 """
@@ -1228,6 +1342,8 @@ class PhotosDB:
self._folder_root_pk = self._dbalbum_details[root_uuid[0]]["pk"]
# build _dbalbum_folders which is in form uuid: [list of parent uuids]
+ # TODO: look at this code...it works but I think I album can only be in a single folder
+ # which means there's a code path that will never get executed
for album, details in self._dbalbum_details.items():
pk_parent = details["parentfolder"]
if pk_parent is None:
@@ -1246,10 +1362,10 @@ class PhotosDB:
for album, details in self._dbalbum_details.items():
# if details["kind"] in [_PHOTOS_5_ALBUM_KIND, _PHOTOS_5_FOLDER_KIND]:
if details["kind"] == _PHOTOS_5_ALBUM_KIND:
- folder_hierarchy = self._build_album_folder_hierarchy(album)
+ folder_hierarchy = self._build_album_folder_hierarchy_5(album)
self._dbalbum_folders[album] = folder_hierarchy
elif details["kind"] == _PHOTOS_5_SHARED_ALBUM_KIND:
- # shared albums can be in folders
+ # shared albums can't be in folders
self._dbalbum_folders[album] = []
if _debug():
@@ -1257,6 +1373,7 @@ class PhotosDB:
logging.debug(pformat(self._dbalbums_album))
logging.debug(pformat(self._dbalbums_uuid))
logging.debug(pformat(self._dbalbum_details))
+ logging.debug(pformat(self._dbalbum_folders))
# get details on keywords
c.execute(
@@ -1747,14 +1864,11 @@ class PhotosDB:
logging.debug("Burst Photos (dbphotos_burst:")
logging.debug(pformat(self._dbphotos_burst))
- def _build_album_folder_hierarchy(self, uuid, folders=None):
+ def _build_album_folder_hierarchy_5(self, uuid, folders=None):
""" recursively build folder/album hierarchy
uuid: uuid of the album/folder being processed
folders: dict holding the folder hierarchy """
- if self._db_version < _PHOTOS_5_VERSION:
- raise AttributeError("Not yet implemented for this DB version")
-
# get parent uuid
parent = self._dbalbum_details[uuid]["parentfolder"]
@@ -1770,10 +1884,50 @@ class PhotosDB:
# recurse to keep building
folders = {parent_uuid: folders}
- folders = self._build_album_folder_hierarchy(parent_uuid, folders=folders)
+ folders = self._build_album_folder_hierarchy_5(parent_uuid, folders=folders)
return folders
def _album_folder_hierarchy_list(self, album_uuid):
+ if self._db_version <= _PHOTOS_4_VERSION:
+ return self._album_folder_hierarchy_list_4(album_uuid)
+ else:
+ return self._album_folder_hierarchy_list_5(album_uuid)
+
+ def _album_folder_hierarchy_list_4(self, album_uuid):
+ """ return hierarchical list of folder names album_uuid is contained in
+ the folder list is in form:
+ ["Top level folder", "sub folder 1", "sub folder 2"]
+ returns empty list of album is not in any folders """
+ # title = photosdb._dbalbum_details[album_uuid]["title"]
+ folders = self._dbalbum_folders[album_uuid]
+
+ def _recurse_folder_hierarchy(folders, hierarchy=[]):
+ """ recursively walk the folders dict to build list of folder hierarchy """
+ if not folders:
+ # empty folder dict (album has no folder hierarchy)
+ return []
+
+ if len(folders) != 1:
+ raise ValueError("Expected only a single key in folders dict")
+
+ folder_uuid = list(folders)[0] # first and only key of dict
+
+ parent_title = self._dbfolder_details[folder_uuid]["name"]
+ hierarchy.append(parent_title)
+
+ folders = folders[folder_uuid]
+ if folders:
+ # still have elements left to recurse
+ hierarchy = _recurse_folder_hierarchy(folders, hierarchy=hierarchy)
+ return hierarchy
+
+ # no elements left to recurse
+ return hierarchy
+
+ hierarchy = _recurse_folder_hierarchy(folders)
+ return hierarchy
+
+ def _album_folder_hierarchy_list_5(self, album_uuid):
""" return hierarchical list of folder names album_uuid is contained in
the folder list is in form:
["Top level folder", "sub folder 1", "sub folder 2"]
@@ -1808,6 +1962,46 @@ class PhotosDB:
return hierarchy
def _album_folder_hierarchy_folderinfo(self, album_uuid):
+ if self._db_version <= _PHOTOS_4_VERSION:
+ return self._album_folder_hierarchy_folderinfo_4(album_uuid)
+ else:
+ return self._album_folder_hierarchy_folderinfo_5(album_uuid)
+
+ def _album_folder_hierarchy_folderinfo_4(self, album_uuid):
+ """ return hierarchical list of FolderInfo objects album_uuid is contained in
+ ["Top level folder", "sub folder 1", "sub folder 2"]
+ returns empty list of album is not in any folders """
+ # title = photosdb._dbalbum_details[album_uuid]["title"]
+ folders = self._dbalbum_folders[album_uuid]
+ # logging.warning(f"uuid = {album_uuid}, folder = {folders}")
+
+ def _recurse_folder_hierarchy(folders, hierarchy=[]):
+ """ recursively walk the folders dict to build list of folder hierarchy """
+ # logging.warning(f"folders={folders},hierarchy = {hierarchy}")
+ if not folders:
+ # empty folder dict (album has no folder hierarchy)
+ return []
+
+ if len(folders) != 1:
+ raise ValueError("Expected only a single key in folders dict")
+
+ folder_uuid = list(folders)[0] # first and only key of dict
+ hierarchy.append(FolderInfo(db=self, uuid=folder_uuid))
+
+ folders = folders[folder_uuid]
+ if folders:
+ # still have elements left to recurse
+ hierarchy = _recurse_folder_hierarchy(folders, hierarchy=hierarchy)
+ return hierarchy
+
+ # no elements left to recurse
+ return hierarchy
+
+ hierarchy = _recurse_folder_hierarchy(folders)
+ # logging.warning(f"hierarchy = {hierarchy}")
+ return hierarchy
+
+ def _album_folder_hierarchy_folderinfo_5(self, album_uuid):
""" return hierarchical list of FolderInfo objects album_uuid is contained in
["Top level folder", "sub folder 1", "sub folder 2"]
returns empty list of album is not in any folders """
diff --git a/tests/Test-10.14.6.photoslibrary/database/RKAlbum_name.skindex b/tests/Test-10.14.6.photoslibrary/database/RKAlbum_name.skindex
index 005f905f..5e617eb2 100644
Binary files a/tests/Test-10.14.6.photoslibrary/database/RKAlbum_name.skindex and b/tests/Test-10.14.6.photoslibrary/database/RKAlbum_name.skindex differ
diff --git a/tests/Test-10.14.6.photoslibrary/database/photos.db b/tests/Test-10.14.6.photoslibrary/database/photos.db
index eee3ba35..6cb8f240 100644
Binary files a/tests/Test-10.14.6.photoslibrary/database/photos.db and b/tests/Test-10.14.6.photoslibrary/database/photos.db differ
diff --git a/tests/Test-10.14.6.photoslibrary/private/com.apple.Photos/appPrivateData.plist b/tests/Test-10.14.6.photoslibrary/private/com.apple.Photos/appPrivateData.plist
index a5cdd9c2..c4268fcf 100644
--- a/tests/Test-10.14.6.photoslibrary/private/com.apple.Photos/appPrivateData.plist
+++ b/tests/Test-10.14.6.photoslibrary/private/com.apple.Photos/appPrivateData.plist
@@ -9,12 +9,14 @@
ExpandedSidebarItemIdentifiers
TopLevelAlbums
+ QtSnVvTkQ%i2z3hB834M1A
TopLevelSlideshows
+ N7eQ4VhfTfeHFp9PPHaJDw
IPXWorkspaceControllerZoomLevelsKey
kZoomLevelIdentifierAlbums
- 10
+ 5
kZoomLevelIdentifierVersions
7
@@ -23,11 +25,11 @@
key
1
lastKnownDisplayName
- Test Album (1)
+ Pumpkin Farm (1)
type
album
uuid
- Uq6qsKihRRSjMHTiD+0Azg
+ xJ8ya3NBRWC24gKhcwwNeQ
lastKnownItemCounts
diff --git a/tests/Test-10.14.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotoAnalysisServicePreferences.plist b/tests/Test-10.14.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotoAnalysisServicePreferences.plist
index 3cc13e1e..f20ca78b 100644
--- a/tests/Test-10.14.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotoAnalysisServicePreferences.plist
+++ b/tests/Test-10.14.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotoAnalysisServicePreferences.plist
@@ -3,8 +3,8 @@
PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate
- 2020-03-27T04:00:09Z
+ 2020-04-18T18:01:02Z
PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate
- 2020-03-27T04:00:10Z
+ 2020-04-18T17:22:55Z
diff --git a/tests/Test-10.14.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph.graphdb b/tests/Test-10.14.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph.graphdb
index 8ec89205..bcc42fc2 100644
Binary files a/tests/Test-10.14.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph.graphdb and b/tests/Test-10.14.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph.graphdb differ
diff --git a/tests/Test-10.14.6.photoslibrary/private/com.apple.photomodel/appPrivateData.plist b/tests/Test-10.14.6.photoslibrary/private/com.apple.photomodel/appPrivateData.plist
index be020436..ecebb467 100644
--- a/tests/Test-10.14.6.photoslibrary/private/com.apple.photomodel/appPrivateData.plist
+++ b/tests/Test-10.14.6.photoslibrary/private/com.apple.photomodel/appPrivateData.plist
@@ -5,7 +5,7 @@
LithiumMessageTracer
LastReportedDate
- 2020-03-15T20:19:24Z
+ 2020-04-17T17:51:16Z
diff --git a/tests/Test-10.14.6.photoslibrary/resources/moments/analysismetadata.plist b/tests/Test-10.14.6.photoslibrary/resources/moments/analysismetadata.plist
index ab262e0d..3592de9e 100644
--- a/tests/Test-10.14.6.photoslibrary/resources/moments/analysismetadata.plist
+++ b/tests/Test-10.14.6.photoslibrary/resources/moments/analysismetadata.plist
@@ -11,6 +11,6 @@
PLLastRevGeoForcedProviderOutOfDateCheckVersionKey
1
PLLastRevGeoVerFileFetchDateKey
- 2020-03-27T03:59:54Z
+ 2020-04-17T17:49:52Z
diff --git a/tests/Test-10.14.6.photoslibrary/resources/moments/historicalmarker.plist b/tests/Test-10.14.6.photoslibrary/resources/moments/historicalmarker.plist
index 89349855..51fe5a2b 100644
--- a/tests/Test-10.14.6.photoslibrary/resources/moments/historicalmarker.plist
+++ b/tests/Test-10.14.6.photoslibrary/resources/moments/historicalmarker.plist
@@ -3,7 +3,7 @@
LastHistoryRowId
- 575
+ 606
LibraryBuildTag
D8C4AAA1-3AB6-4A65-BEBD-99CC3E5D433E
LibrarySchemaVersion
diff --git a/tests/Test-10.14.6.photoslibrary/resources/moments/needsanalysis b/tests/Test-10.14.6.photoslibrary/resources/moments/needsanalysis
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/Test-10.14.6.photoslibrary/resources/recovery/Info.plist b/tests/Test-10.14.6.photoslibrary/resources/recovery/Info.plist
index 6f690882..19fe5a2e 100644
--- a/tests/Test-10.14.6.photoslibrary/resources/recovery/Info.plist
+++ b/tests/Test-10.14.6.photoslibrary/resources/recovery/Info.plist
@@ -9,7 +9,7 @@
HistoricalMarker
LastHistoryRowId
- 575
+ 606
LibraryBuildTag
D8C4AAA1-3AB6-4A65-BEBD-99CC3E5D433E
LibrarySchemaVersion
@@ -24,7 +24,7 @@
SnapshotCompletedDate
2019-07-27T13:16:43Z
SnapshotLastValidated
- 2020-03-27T04:02:59Z
+ 2020-04-17T17:51:16Z
SnapshotTables
diff --git a/tests/Test-10.14.6.photoslibrary/resources/recovery/RKAlbum/0000000000.lij b/tests/Test-10.14.6.photoslibrary/resources/recovery/RKAlbum/0000000000.lij
index 709d4fd4..de69f777 100644
Binary files a/tests/Test-10.14.6.photoslibrary/resources/recovery/RKAlbum/0000000000.lij and b/tests/Test-10.14.6.photoslibrary/resources/recovery/RKAlbum/0000000000.lij differ
diff --git a/tests/Test-10.14.6.photoslibrary/resources/recovery/RKAlbumVersion/0000000000.lij b/tests/Test-10.14.6.photoslibrary/resources/recovery/RKAlbumVersion/0000000000.lij
index 90d13f94..792da1a1 100644
Binary files a/tests/Test-10.14.6.photoslibrary/resources/recovery/RKAlbumVersion/0000000000.lij and b/tests/Test-10.14.6.photoslibrary/resources/recovery/RKAlbumVersion/0000000000.lij differ
diff --git a/tests/Test-10.14.6.photoslibrary/resources/recovery/RKCustomSortOrder/0000000000.lij b/tests/Test-10.14.6.photoslibrary/resources/recovery/RKCustomSortOrder/0000000000.lij
index c0641fd7..66cad110 100644
Binary files a/tests/Test-10.14.6.photoslibrary/resources/recovery/RKCustomSortOrder/0000000000.lij and b/tests/Test-10.14.6.photoslibrary/resources/recovery/RKCustomSortOrder/0000000000.lij differ
diff --git a/tests/Test-10.14.6.photoslibrary/resources/recovery/RKFolder/0000000000.lij b/tests/Test-10.14.6.photoslibrary/resources/recovery/RKFolder/0000000000.lij
index 8141ed17..3f5a5201 100644
Binary files a/tests/Test-10.14.6.photoslibrary/resources/recovery/RKFolder/0000000000.lij and b/tests/Test-10.14.6.photoslibrary/resources/recovery/RKFolder/0000000000.lij differ
diff --git a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite
index cd3245d1..8fc28430 100644
Binary files a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite and b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite differ
diff --git a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-shm b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-shm
index 67bd1a3b..fe9ac284 100644
Binary files a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal
index b7a2c385..e69de29b 100644
Binary files a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite
index 7b8acdfc..495b1e98 100644
Binary files a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite and b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite differ
diff --git a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm
index ff6d518c..ef3db305 100644
Binary files a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db
index d584f58a..50019713 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db and b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm
index af889512..697c3351 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm
index ff1b23e6..182adbd5 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal
index f1af3e7d..666a4bdd 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm
index 77b3c69b..09b27bc5 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-wal
index 958d2087..12257824 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm
index 350fc8cb..3bd83e4c 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-wal
index 101eeb1d..a0688cd9 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm
index 42c6fc29..6a3d111e 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal
index 0c42aec5..668a6e0b 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist
index 36000c8a..abb9dc95 100644
--- a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist
+++ b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist
@@ -3,24 +3,24 @@
BackgroundHighlightCollection
- 2020-04-16T17:31:22Z
+ 2020-04-17T14:33:32Z
BackgroundHighlightEnrichment
- 2020-04-16T17:31:21Z
+ 2020-04-17T14:33:32Z
BackgroundJobAssetRevGeocode
- 2020-04-16T17:31:22Z
+ 2020-04-17T14:33:33Z
BackgroundJobSearch
- 2020-04-16T17:31:22Z
+ 2020-04-17T14:33:33Z
BackgroundPeopleSuggestion
- 2020-04-16T17:31:21Z
+ 2020-04-17T14:33:31Z
BackgroundUserBehaviorProcessor
- 2020-04-16T06:52:39Z
+ 2020-04-17T07:32:04Z
PhotoAnalysisGraphLastBackgroundGraphConsistencyUpdateJobDateKey
- 2020-04-16T17:38:41Z
+ 2020-04-17T14:33:37Z
PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate
- 2020-04-16T06:52:38Z
+ 2020-04-17T07:32:00Z
PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate
- 2020-04-16T17:31:22Z
+ 2020-04-17T14:33:34Z
SiriPortraitDonation
- 2020-04-16T06:52:39Z
+ 2020-04-17T07:32:04Z
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb-shm
index fe9ac284..ca5a3e01 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/construction-photosgraph.kgdb-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb-shm
index fe9ac284..ca5a3e01 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph-tmp.kgdb-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb
index 97af9cca..2f9b3080 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/PhotoAnalysisServicePreferences.plist b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/PhotoAnalysisServicePreferences.plist
index 401beb19..61af2d00 100644
--- a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/PhotoAnalysisServicePreferences.plist
+++ b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/PhotoAnalysisServicePreferences.plist
@@ -3,8 +3,8 @@
FaceIDModelLastGenerationKey
- 2020-04-16T06:52:40Z
+ 2020-04-17T07:32:07Z
LastContactClassificationKey
- 2020-04-16T06:52:42Z
+ 2020-04-17T07:32:12Z
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/vnpersonsmodel.bin b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/vnpersonsmodel.bin
index 33d1e7bf..8cbdaf14 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/vnpersonsmodel.bin and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/vnpersonsmodel.bin differ
diff --git a/tests/test_albums_folders_mojave_10_14_6.py b/tests/test_albums_folders_mojave_10_14_6.py
index 41971c2c..91bbc44f 100644
--- a/tests/test_albums_folders_mojave_10_14_6.py
+++ b/tests/test_albums_folders_mojave_10_14_6.py
@@ -4,32 +4,33 @@ from osxphotos._constants import _UNKNOWN_PERSON
PHOTOS_DB = "./tests/Test-10.14.6.photoslibrary/database/photos.db"
-# TOP_LEVEL_FOLDERS = ["Folder1"]
+TOP_LEVEL_FOLDERS = ["Folder1"]
-# TOP_LEVEL_CHILDREN = ["SubFolder1", "SubFolder2"]
+TOP_LEVEL_CHILDREN = ["SubFolder1", "SubFolder2"]
-# FOLDER_ALBUM_DICT = {"Folder1": [], "SubFolder1": [], "SubFolder2": ["AlbumInFolder"]}
+FOLDER_ALBUM_DICT = {"Folder1": [], "SubFolder1": [], "SubFolder2": ["AlbumInFolder"]}
-# ALBUM_NAMES = ["Pumpkin Farm", "AlbumInFolder", "Test Album", "Test Album"]
-ALBUM_NAMES = ["Pumpkin Farm", "Test Album", "Test Album (1)"]
+ALBUM_NAMES = ["Pumpkin Farm", "AlbumInFolder", "Test Album", "Test Album (1)"]
-# ALBUM_PARENT_DICT = {
-# "Pumpkin Farm": None,
-# "AlbumInFolder": "SubFolder2",
-# "Test Album": None,
-# }
+ALBUM_PARENT_DICT = {
+ "Pumpkin Farm": None,
+ "AlbumInFolder": "SubFolder2",
+ "Test Album": None,
+ "Test Album (1)": None,
+}
-# ALBUM_FOLDER_NAMES_DICT = {
-# "Pumpkin Farm": [],
-# "AlbumInFolder": ["Folder1", "SubFolder2"],
-# "Test Album": [],
-# }
+ALBUM_FOLDER_NAMES_DICT = {
+ "Pumpkin Farm": [],
+ "AlbumInFolder": ["Folder1", "SubFolder2"],
+ "Test Album": [],
+ "Test Album (1)": [],
+}
ALBUM_LEN_DICT = {
"Pumpkin Farm": 3,
"Test Album": 1,
"Test Album (1)": 1,
- # "AlbumInFolder": 2,
+ "AlbumInFolder": 1,
}
ALBUM_PHOTO_UUID_DICT = {
@@ -40,10 +41,7 @@ ALBUM_PHOTO_UUID_DICT = {
],
"Test Album": ["8SOE9s0XQVGsuq4ONohTng"],
"Test Album (1)": ["15uNd7%8RguTEgNPKHfTWw"],
- # "AlbumInFolder": [
- # "3DD2C897-F19E-4CA6-8C22-B027D5A71907",
- # "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
- # ],
+ "AlbumInFolder": ["15uNd7%8RguTEgNPKHfTWw"],
}
UUID_DICT = {"two_albums": "8SOE9s0XQVGsuq4ONohTng"}
@@ -51,62 +49,57 @@ UUID_DICT = {"two_albums": "8SOE9s0XQVGsuq4ONohTng"}
######### Test FolderInfo ##########
-def test_folders_1(caplog):
+def test_folders_1():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
folders = photosdb.folders
- assert folders == []
- assert "Folders not yet implemented for this DB version" in caplog.text
- # # top level folders
- # folders = photosdb.folders
- # assert len(folders) == 1
+ # top level folders
+ folders = photosdb.folder_info
+ assert len(folders) == 1
- # # check folder names
- # folder_names = [f.title for f in folders]
- # assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
+ # check folder names
+ folder_names = [f.title for f in folders]
+ assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
-def test_folder_names(caplog):
+def test_folder_names():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
# check folder names
folder_names = photosdb.folders
- assert folder_names == []
- assert "Folders not yet implemented for this DB version" in caplog.text
- # assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
+ assert folder_names == TOP_LEVEL_FOLDERS
+ assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
-@pytest.mark.skip(reason="Folders not yet impleted in Photos < 5")
def test_folders_len():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
# top level folders
- folders = photosdb.folders
+ folders = photosdb.folder_info
assert len(folders[0]) == len(TOP_LEVEL_CHILDREN)
-@pytest.mark.skip(reason="Folders not yet impleted in Photos < 5")
def test_folders_children():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
# top level folders
- folders = photosdb.folders
+ folders = photosdb.folder_info
# children of top level folder
- children = folders[0].folders
+ children = folders[0].subfolders
children_names = [f.title for f in children]
assert sorted(children_names) == sorted(TOP_LEVEL_CHILDREN)
- for child in folders[0].folders:
+ for child in folders[0].subfolders:
# check valid children FolderInfo
assert child.parent
assert child.parent.uuid == folders[0].uuid
@@ -116,38 +109,36 @@ def test_folders_children():
assert sorted(folder_names) == sorted(TOP_LEVEL_FOLDERS)
-@pytest.mark.skip(reason="Folders not yet impleted in Photos < 5")
def test_folders_parent():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
# top level folders
- folders = photosdb.folders
+ folders = photosdb.folder_info
# parent of top level folder should be none
for folder in folders:
assert folder.parent is None
- for child in folder.folders:
+ for child in folder.subfolders:
# children's parent uuid should match folder uuid
assert child.parent
assert child.parent.uuid == folder.uuid
-@pytest.mark.skip(reason="Folders not yet impleted in Photos < 5")
def test_folders_albums():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
# top level folders
- folders = photosdb.folders
+ folders = photosdb.folder_info
for folder in folders:
name = folder.title
albums = [a.title for a in folder.album_info]
assert sorted(albums) == sorted(FOLDER_ALBUM_DICT[name])
- for child in folder.folders:
+ for child in folder.subfolders:
name = child.title
albums = [a.title for a in child.album_info]
assert sorted(albums) == sorted(FOLDER_ALBUM_DICT[name])
@@ -162,14 +153,14 @@ def test_albums_1():
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
albums = photosdb.album_info
- assert len(albums) == 3
+ assert len(albums) == 4
# check names
album_names = [a.title for a in albums]
assert sorted(album_names) == sorted(ALBUM_NAMES)
-def test_albums_parent(caplog):
+def test_albums_parent():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
@@ -178,11 +169,10 @@ def test_albums_parent(caplog):
for album in albums:
parent = album.parent.title if album.parent else None
- assert "Folders not yet implemented for this DB version" in caplog.text
- # assert parent == ALBUM_PARENT_DICT[album.title]
+ assert parent == ALBUM_PARENT_DICT[album.title]
-def test_albums_folder_names(caplog):
+def test_albums_folder_names():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
@@ -191,11 +181,10 @@ def test_albums_folder_names(caplog):
for album in albums:
folder_names = album.folder_names
- assert "Folders not yet implemented for this DB version" in caplog.text
- # assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
+ assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
-def test_albums_folders(caplog):
+def test_albums_folders():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
@@ -204,9 +193,8 @@ def test_albums_folders(caplog):
for album in albums:
folders = album.folder_list
- assert "Folders not yet implemented for this DB version" in caplog.text
- # folder_names = [f.title for f in folders]
- # assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
+ folder_names = [f.title for f in folders]
+ assert folder_names == ALBUM_FOLDER_NAMES_DICT[album.title]
def test_albums_len():
diff --git a/tests/test_cli.py b/tests/test_cli.py
index f0828d02..20f9eb78 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -780,7 +780,7 @@ def test_no_folder_2_15():
assert item["albums"] == ["AlbumInFolder"]
-def test_no_folder_1_14(caplog):
+def test_no_folder_1_14():
# test --folder on 10.14
import json
import os
@@ -797,6 +797,5 @@ def test_no_folder_1_14(caplog):
)
assert result.exit_code == 0
json_got = json.loads(result.output)
-
- assert len(json_got) == 0 # single element
- assert "not yet implemented" in caplog.text
+ assert len(json_got) == 1 # single element
+ assert json_got[0]["uuid"] == "15uNd7%8RguTEgNPKHfTWw"
diff --git a/tests/test_mojave_10_14_6.py b/tests/test_mojave_10_14_6.py
index b5ad8417..71de658f 100644
--- a/tests/test_mojave_10_14_6.py
+++ b/tests/test_mojave_10_14_6.py
@@ -18,7 +18,7 @@ KEYWORDS = [
"United Kingdom",
]
PERSONS = ["Katie", "Suzy", "Maria"]
-ALBUMS = ["Pumpkin Farm", "Test Album", "Test Album (1)"]
+ALBUMS = ["Pumpkin Farm", "AlbumInFolder", "Test Album", "Test Album (1)"]
KEYWORDS_DICT = {
"Kids": 4,
"wedding": 2,
@@ -31,7 +31,12 @@ KEYWORDS_DICT = {
"United Kingdom": 1,
}
PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1}
-ALBUM_DICT = {"Pumpkin Farm": 3, "Test Album": 1, "Test Album (1)": 1}
+ALBUM_DICT = {
+ "Pumpkin Farm": 3,
+ "AlbumInFolder": 1,
+ "Test Album": 1,
+ "Test Album (1)": 1,
+}
UUID_DICT = {
"favorite": "6bxcNnzRQKGnK4uPrCJ9UQ",
@@ -131,7 +136,9 @@ def test_attributes():
)
assert p.description == "Girl holding pumpkin"
assert p.title == "I found one!"
- assert p.albums == ["Pumpkin Farm", "Test Album (1)"]
+ assert sorted(p.albums) == sorted(
+ ["Pumpkin Farm", "AlbumInFolder", "Test Album (1)"]
+ )
assert p.persons == ["Katie"]
assert p.path.endswith(
"/tests/Test-10.14.6.photoslibrary/Masters/2019/07/27/20190727-131650/Pumkins2.jpg"
diff --git a/tests/test_shared_mojave_10_14_6.py b/tests/test_shared_mojave_10_14_6.py
index bb52b978..b78e592b 100644
--- a/tests/test_shared_mojave_10_14_6.py
+++ b/tests/test_shared_mojave_10_14_6.py
@@ -7,8 +7,13 @@ 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-10.14.6.photoslibrary"
-ALBUMS = ["Pumpkin Farm", "Test Album", "Test Album (1)"]
-ALBUM_DICT = {"Pumpkin Farm": 3, "Test Album": 1, "Test Album (1)": 1}
+ALBUMS = ["Pumpkin Farm", "AlbumInFolder", "Test Album", "Test Album (1)"]
+ALBUM_DICT = {
+ "Pumpkin Farm": 3,
+ "AlbumInFolder": 1,
+ "Test Album": 1,
+ "Test Album (1)": 1,
+}
def test_album_names():
diff --git a/tests/test_template.py b/tests/test_template.py
index 4d3fd666..81242795 100644
--- a/tests/test_template.py
+++ b/tests/test_template.py
@@ -15,7 +15,7 @@ UUID_DICT = {
"0_2_0": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
"folder_album_1": "3DD2C897-F19E-4CA6-8C22-B027D5A71907",
"folder_album_no_folder": "D79B8D77-BFFC-460B-9312-034F2877D35B",
- "mojave_no_folder": "15uNd7%8RguTEgNPKHfTWw",
+ "mojave_album_1": "15uNd7%8RguTEgNPKHfTWw",
}
TEMPLATE_VALUES = {
@@ -341,17 +341,16 @@ def test_subst_multi_folder_albums_2():
def test_subst_multi_folder_albums_3(caplog):
- """ Test substitutions for folder_album on < Photos 5 (not implemented) """
+ """ Test substitutions for folder_album on < Photos 5 """
import osxphotos
from osxphotos.template import render_filepath_template
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB_14_6)
# photo in an album in a folder
- photo = photosdb.photos(uuid=[UUID_DICT["mojave_no_folder"]])[0]
+ photo = photosdb.photos(uuid=[UUID_DICT["mojave_album_1"]])[0]
template = "{folder_album}"
- expected = ["Pumpkin Farm", "Test Album (1)"]
+ expected = ["Folder1/SubFolder2/AlbumInFolder", "Pumpkin Farm", "Test Album (1)"]
rendered, unknown = render_filepath_template(template, photo)
assert sorted(rendered) == sorted(expected)
assert unknown == []
- assert "not yet implemented" in caplog.text