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