Refactored album code in photosdb to fix issue #169
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.29.23"
|
__version__ = "0.29.24"
|
||||||
|
|||||||
@@ -348,16 +348,15 @@ class PhotoInfo:
|
|||||||
return self._albums
|
return self._albums
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self._albums = []
|
self._albums = []
|
||||||
if self._db._db_version <= _PHOTOS_4_VERSION:
|
album_kinds = (
|
||||||
album_kinds = [_PHOTOS_4_ALBUM_KIND]
|
[_PHOTOS_4_ALBUM_KIND]
|
||||||
kind_field = "albumSubclass"
|
if self._db._db_version <= _PHOTOS_4_VERSION
|
||||||
else:
|
else [_PHOTOS_5_ALBUM_KIND, _PHOTOS_5_SHARED_ALBUM_KIND]
|
||||||
album_kinds = [_PHOTOS_5_ALBUM_KIND, _PHOTOS_5_SHARED_ALBUM_KIND]
|
)
|
||||||
kind_field = "kind"
|
|
||||||
|
|
||||||
for album in self._info["albums"]:
|
for album in self._info["albums"]:
|
||||||
detail = self._db._dbalbum_details[album]
|
detail = self._db._dbalbum_details[album]
|
||||||
if detail[kind_field] in album_kinds and not detail["intrash"]:
|
if detail["kind"] in album_kinds and not detail["intrash"]:
|
||||||
self._albums.append(detail["title"])
|
self._albums.append(detail["title"])
|
||||||
return self._albums
|
return self._albums
|
||||||
|
|
||||||
@@ -368,16 +367,15 @@ class PhotoInfo:
|
|||||||
return self._album_info
|
return self._album_info
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self._album_info = []
|
self._album_info = []
|
||||||
if self._db._db_version <= _PHOTOS_4_VERSION:
|
album_kinds = (
|
||||||
album_kinds = [_PHOTOS_4_ALBUM_KIND]
|
[_PHOTOS_4_ALBUM_KIND]
|
||||||
kind_field = "albumSubclass"
|
if self._db._db_version <= _PHOTOS_4_VERSION
|
||||||
else:
|
else [_PHOTOS_5_ALBUM_KIND, _PHOTOS_5_SHARED_ALBUM_KIND]
|
||||||
album_kinds = [_PHOTOS_5_ALBUM_KIND, _PHOTOS_5_SHARED_ALBUM_KIND]
|
)
|
||||||
kind_field = "kind"
|
|
||||||
|
|
||||||
for album in self._info["albums"]:
|
for album in self._info["albums"]:
|
||||||
detail = self._db._dbalbum_details[album]
|
detail = self._db._dbalbum_details[album]
|
||||||
if detail[kind_field] in album_kinds and not detail["intrash"]:
|
if detail["kind"] in album_kinds and not detail["intrash"]:
|
||||||
self._album_info.append(AlbumInfo(db=self._db, uuid=album))
|
self._album_info.append(AlbumInfo(db=self._db, uuid=album))
|
||||||
return self._album_info
|
return self._album_info
|
||||||
|
|
||||||
|
|||||||
@@ -311,18 +311,16 @@ class PhotosDB:
|
|||||||
def albums_as_dict(self):
|
def albums_as_dict(self):
|
||||||
""" return albums as dict of albums, count in reverse sorted order (descending) """
|
""" return albums as dict of albums, count in reverse sorted order (descending) """
|
||||||
albums = {}
|
albums = {}
|
||||||
album_keys = [
|
album_keys = self._get_album_uuids(shared=False)
|
||||||
k
|
for album in album_keys:
|
||||||
for k in self._dbalbums_album.keys()
|
title = self._dbalbum_details[album]["title"]
|
||||||
if self._dbalbum_details[k]["cloudownerhashedpersonid"] is None
|
if album in self._dbalbums_album:
|
||||||
and not self._dbalbum_details[k]["intrash"]
|
try:
|
||||||
]
|
albums[title] += len(self._dbalbums_album[album])
|
||||||
for k in album_keys:
|
except KeyError:
|
||||||
title = self._dbalbum_details[k]["title"]
|
albums[title] = len(self._dbalbums_album[album])
|
||||||
if title in albums:
|
|
||||||
albums[title] += len(self._dbalbums_album[k])
|
|
||||||
else:
|
else:
|
||||||
albums[title] = len(self._dbalbums_album[k])
|
albums[title] = 0 # empty album
|
||||||
albums = dict(sorted(albums.items(), key=lambda kv: kv[1], reverse=True))
|
albums = dict(sorted(albums.items(), key=lambda kv: kv[1], reverse=True))
|
||||||
return albums
|
return albums
|
||||||
|
|
||||||
@@ -331,25 +329,17 @@ class PhotosDB:
|
|||||||
""" returns shared albums as dict of albums, count in reverse sorted order (descending)
|
""" returns shared albums as dict of albums, count in reverse sorted order (descending)
|
||||||
valid only on Photos 5; on Photos <= 4, prints warning and returns empty dict """
|
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_4_VERSION:
|
|
||||||
logging.warning(
|
|
||||||
f"albums_shared not implemented for Photos versions < {_PHOTOS_5_VERSION}"
|
|
||||||
)
|
|
||||||
return {}
|
|
||||||
|
|
||||||
albums = {}
|
albums = {}
|
||||||
album_keys = [
|
album_keys = self._get_album_uuids(shared=True)
|
||||||
k
|
for album in album_keys:
|
||||||
for k in self._dbalbums_album.keys()
|
title = self._dbalbum_details[album]["title"]
|
||||||
if self._dbalbum_details[k]["cloudownerhashedpersonid"] is not None
|
if album in self._dbalbums_album:
|
||||||
]
|
try:
|
||||||
for k in album_keys:
|
albums[title] += len(self._dbalbums_album[album])
|
||||||
title = self._dbalbum_details[k]["title"]
|
except KeyError:
|
||||||
if title in albums:
|
albums[title] = len(self._dbalbums_album[album])
|
||||||
albums[title] += len(self._dbalbums_album[k])
|
|
||||||
else:
|
else:
|
||||||
albums[title] = len(self._dbalbums_album[k])
|
albums[title] = 0 # empty album
|
||||||
albums = dict(sorted(albums.items(), key=lambda kv: kv[1], reverse=True))
|
albums = dict(sorted(albums.items(), key=lambda kv: kv[1], reverse=True))
|
||||||
return albums
|
return albums
|
||||||
|
|
||||||
@@ -410,32 +400,28 @@ class PhotosDB:
|
|||||||
@property
|
@property
|
||||||
def album_info(self):
|
def album_info(self):
|
||||||
""" return list of AlbumInfo objects for each album in the photos database """
|
""" return list of AlbumInfo objects for each album in the photos database """
|
||||||
|
try:
|
||||||
return [
|
return self._album_info
|
||||||
AlbumInfo(db=self, uuid=album)
|
except AttributeError:
|
||||||
for album in self._dbalbums_album.keys()
|
self._album_info = [
|
||||||
if self._dbalbum_details[album]["cloudownerhashedpersonid"] is None
|
AlbumInfo(db=self, uuid=album)
|
||||||
and not self._dbalbum_details[album]["intrash"]
|
for album in self._get_album_uuids(shared=False)
|
||||||
]
|
]
|
||||||
|
return self._album_info
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def album_info_shared(self):
|
def album_info_shared(self):
|
||||||
""" return list of AlbumInfo objects for each shared album in the photos database
|
""" return list of AlbumInfo objects for each shared album in the photos database
|
||||||
only valid for Photos 5; on Photos <= 4, prints warning and returns empty list """
|
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 _dbalbum_details[key]["cloudownerhashedpersonid"] is not None, then it's a shared album
|
||||||
|
try:
|
||||||
if self._db_version <= _PHOTOS_4_VERSION:
|
return self._album_info_shared
|
||||||
logging.warning(
|
except AttributeError:
|
||||||
f"albums_shared not implemented for Photos versions < {_PHOTOS_5_VERSION}"
|
self._album_info_shared = [
|
||||||
)
|
AlbumInfo(db=self, uuid=album)
|
||||||
return []
|
for album in self._get_album_uuids(shared=True)
|
||||||
|
]
|
||||||
return [
|
return self._album_info_shared
|
||||||
AlbumInfo(db=self, uuid=album)
|
|
||||||
for album in self._dbalbums_album.keys()
|
|
||||||
if self._dbalbum_details[album]["cloudownerhashedpersonid"] is not None
|
|
||||||
and not self._dbalbum_details[album]["intrash"]
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def albums(self):
|
def albums(self):
|
||||||
@@ -444,13 +430,11 @@ class PhotosDB:
|
|||||||
# Could be more than one album with same name
|
# Could be more than one album with same name
|
||||||
# Right now, they are treated as same album and photos are combined from albums with same name
|
# Right now, they are treated as same album and photos are combined from albums with same name
|
||||||
|
|
||||||
albums = {
|
try:
|
||||||
self._dbalbum_details[album]["title"]
|
return self._albums
|
||||||
for album in self._dbalbums_album.keys()
|
except AttributeError:
|
||||||
if self._dbalbum_details[album]["cloudownerhashedpersonid"] is None
|
self._albums = self._get_albums(shared=False)
|
||||||
and not self._dbalbum_details[album]["intrash"]
|
return self._albums
|
||||||
}
|
|
||||||
return list(albums)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def albums_shared(self):
|
def albums_shared(self):
|
||||||
@@ -462,19 +446,11 @@ class PhotosDB:
|
|||||||
|
|
||||||
# if _dbalbum_details[key]["cloudownerhashedpersonid"] is not None, then it's a shared album
|
# if _dbalbum_details[key]["cloudownerhashedpersonid"] is not None, then it's a shared album
|
||||||
|
|
||||||
if self._db_version <= _PHOTOS_4_VERSION:
|
try:
|
||||||
logging.warning(
|
return self._albums_shared
|
||||||
f"album_names_shared not implemented for Photos versions < {_PHOTOS_5_VERSION}"
|
except AttributeError:
|
||||||
)
|
self._albums_shared = self._get_albums(shared=True)
|
||||||
return []
|
return self._albums_shared
|
||||||
|
|
||||||
albums = {
|
|
||||||
self._dbalbum_details[album]["title"]
|
|
||||||
for album in self._dbalbums_album.keys()
|
|
||||||
if self._dbalbum_details[album]["cloudownerhashedpersonid"] is not None
|
|
||||||
and not self._dbalbum_details[album]["intrash"]
|
|
||||||
}
|
|
||||||
return list(albums)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def db_version(self):
|
def db_version(self):
|
||||||
@@ -634,6 +610,8 @@ class PhotosDB:
|
|||||||
"folderUuid": album[5],
|
"folderUuid": album[5],
|
||||||
"albumType": album[6],
|
"albumType": album[6],
|
||||||
"albumSubclass": album[7],
|
"albumSubclass": album[7],
|
||||||
|
# for compatability with Photos 5 where album kind is ZKIND
|
||||||
|
"kind": album[7],
|
||||||
}
|
}
|
||||||
|
|
||||||
# get details about folders
|
# get details about folders
|
||||||
@@ -2098,6 +2076,65 @@ class PhotosDB:
|
|||||||
hierarchy = _recurse_folder_hierarchy(folders)
|
hierarchy = _recurse_folder_hierarchy(folders)
|
||||||
return hierarchy
|
return hierarchy
|
||||||
|
|
||||||
|
def _get_album_uuids(self, shared=False):
|
||||||
|
""" Return list of album UUIDs found in photos database
|
||||||
|
|
||||||
|
Filters out albums in the trash and any special album types
|
||||||
|
|
||||||
|
Args:
|
||||||
|
shared: boolean; if True, returns shared albums, else normal albums
|
||||||
|
|
||||||
|
Returns: list of album names
|
||||||
|
"""
|
||||||
|
if self._db_version <= _PHOTOS_4_VERSION:
|
||||||
|
version4 = True
|
||||||
|
if shared:
|
||||||
|
logging.warning(
|
||||||
|
f"Shared albums not implemented for Photos library version {self._db_version}"
|
||||||
|
)
|
||||||
|
return [] # not implemented for _PHOTOS_4_VERSION
|
||||||
|
else:
|
||||||
|
album_kind = _PHOTOS_4_ALBUM_KIND
|
||||||
|
else:
|
||||||
|
version4 = False
|
||||||
|
album_kind = _PHOTOS_5_SHARED_ALBUM_KIND if shared else _PHOTOS_5_ALBUM_KIND
|
||||||
|
|
||||||
|
album_list = []
|
||||||
|
# look through _dbalbum_details because _dbalbums_album won't have empty albums it
|
||||||
|
for album, detail in self._dbalbum_details.items():
|
||||||
|
if (
|
||||||
|
detail["kind"] == album_kind
|
||||||
|
and not detail["intrash"]
|
||||||
|
and (
|
||||||
|
(shared and detail["cloudownerhashedpersonid"] is not None)
|
||||||
|
or (not shared and detail["cloudownerhashedpersonid"] is None)
|
||||||
|
)
|
||||||
|
and (
|
||||||
|
# in Photos 4, special albums like "printAlbum" have kind _PHOTOS_4_ALBUM_KIND
|
||||||
|
# but should not be listed here; they can be distinguished by looking
|
||||||
|
# for folderUuid of _PHOTOS_4_ROOT_FOLDER as opposed to _PHOTOS_4_TOP_LEVEL_ALBUM
|
||||||
|
(version4 and detail["folderUuid"] != _PHOTOS_4_ROOT_FOLDER)
|
||||||
|
or not version4
|
||||||
|
)
|
||||||
|
):
|
||||||
|
album_list.append(album)
|
||||||
|
return album_list
|
||||||
|
|
||||||
|
def _get_albums(self, shared=False):
|
||||||
|
""" Return list of album titles found in photos database
|
||||||
|
Albums may have duplicate titles -- these will be treated as a single album.
|
||||||
|
|
||||||
|
Filters out albums in the trash and any special album types
|
||||||
|
|
||||||
|
Args:
|
||||||
|
shared: boolean; if True, returns shared albums, else normal albums
|
||||||
|
|
||||||
|
Returns: list of album names
|
||||||
|
"""
|
||||||
|
|
||||||
|
album_uuids = self._get_album_uuids(shared=shared)
|
||||||
|
return list({self._dbalbum_details[album]["title"] for album in album_uuids})
|
||||||
|
|
||||||
def photos(
|
def photos(
|
||||||
self,
|
self,
|
||||||
keywords=None,
|
keywords=None,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ KEYWORDS = [
|
|||||||
"United Kingdom",
|
"United Kingdom",
|
||||||
]
|
]
|
||||||
PERSONS = ["Katie", "Suzy", "Maria"]
|
PERSONS = ["Katie", "Suzy", "Maria"]
|
||||||
ALBUMS = ["Pumpkin Farm", "Last Import", "AlbumInFolder"]
|
ALBUMS = ["Pumpkin Farm", "AlbumInFolder"]
|
||||||
KEYWORDS_DICT = {
|
KEYWORDS_DICT = {
|
||||||
"Kids": 4,
|
"Kids": 4,
|
||||||
"wedding": 2,
|
"wedding": 2,
|
||||||
@@ -29,7 +29,7 @@ KEYWORDS_DICT = {
|
|||||||
"United Kingdom": 1,
|
"United Kingdom": 1,
|
||||||
}
|
}
|
||||||
PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1}
|
PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 1}
|
||||||
ALBUM_DICT = {"Pumpkin Farm": 3, "Last Import": 1, "AlbumInFolder": 1}
|
ALBUM_DICT = {"Pumpkin Farm": 3, "AlbumInFolder": 1}
|
||||||
|
|
||||||
|
|
||||||
def test_init():
|
def test_init():
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ ALBUMS = [
|
|||||||
"AlbumInFolder",
|
"AlbumInFolder",
|
||||||
"Raw",
|
"Raw",
|
||||||
"I have a deleted twin", # there's an empty album with same name that has been deleted
|
"I have a deleted twin", # there's an empty album with same name that has been deleted
|
||||||
|
"EmptyAlbum",
|
||||||
]
|
]
|
||||||
KEYWORDS_DICT = {
|
KEYWORDS_DICT = {
|
||||||
"Kids": 4,
|
"Kids": 4,
|
||||||
@@ -47,6 +48,7 @@ ALBUM_DICT = {
|
|||||||
"AlbumInFolder": 2,
|
"AlbumInFolder": 2,
|
||||||
"Raw": 4,
|
"Raw": 4,
|
||||||
"I have a deleted twin": 1,
|
"I have a deleted twin": 1,
|
||||||
|
"EmptyAlbum": 0,
|
||||||
} # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
} # Note: there are 2 albums named "Test Album" for testing duplicate album names
|
||||||
|
|
||||||
UUID_DICT = {
|
UUID_DICT = {
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ ALBUMS_JSON = {
|
|||||||
"AlbumInFolder": 2,
|
"AlbumInFolder": 2,
|
||||||
"Test Album": 2,
|
"Test Album": 2,
|
||||||
"I have a deleted twin": 1,
|
"I have a deleted twin": 1,
|
||||||
|
"EmptyAlbum": 0,
|
||||||
},
|
},
|
||||||
"shared albums": {},
|
"shared albums": {},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user