Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ebf995833 | ||
|
|
538bac7ade | ||
|
|
32806c8459 | ||
|
|
cfabd0dbea | ||
|
|
a23259948c | ||
|
|
1212fad4ad | ||
|
|
567abe3311 |
23
CHANGELOG.md
23
CHANGELOG.md
@@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file. Dates are d
|
|||||||
|
|
||||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||||
|
|
||||||
|
#### [v0.29.24](https://github.com/RhetTbull/osxphotos/compare/v0.29.23...v0.29.24)
|
||||||
|
|
||||||
|
> 21 June 2020
|
||||||
|
|
||||||
|
- Refactored album code in photosdb to fix issue #169 [`cfabd0d`](https://github.com/RhetTbull/osxphotos/commit/cfabd0dbead62c8ab6a774899239e5da5bfe1203)
|
||||||
|
|
||||||
|
#### [v0.29.23](https://github.com/RhetTbull/osxphotos/compare/v0.29.22...v0.29.23)
|
||||||
|
|
||||||
|
> 20 June 2020
|
||||||
|
|
||||||
|
- Fixed PhotoInfo.albums, album_info for issue #169 [`1212fad`](https://github.com/RhetTbull/osxphotos/commit/1212fad4adde0b4c6b2887392eed829d8d96d61d)
|
||||||
|
|
||||||
|
#### [v0.29.22](https://github.com/RhetTbull/osxphotos/compare/v0.29.19...v0.29.22)
|
||||||
|
|
||||||
|
> 19 June 2020
|
||||||
|
|
||||||
|
- Don't raise KeyError when SystemLibraryPath is absent [`#168`](https://github.com/RhetTbull/osxphotos/pull/168)
|
||||||
|
- Added check for export db in directory branch, closes #164 [`#164`](https://github.com/RhetTbull/osxphotos/issues/164)
|
||||||
|
- Added OSXPhotosDB.get_db_connection() [`43d28e7`](https://github.com/RhetTbull/osxphotos/commit/43d28e78f394fa33f8d88f64b56b7dc7258cd454)
|
||||||
|
- Added show() to photos_repl.py [`e98c3fe`](https://github.com/RhetTbull/osxphotos/commit/e98c3fe42912ac16d13675bf14154981089d41ea)
|
||||||
|
- Fixed get_last_library_path and get_system_library_path to not raise KeyError [`5a83218`](https://github.com/RhetTbull/osxphotos/commit/5a832181f73e082927c80864f2063e554906b06b)
|
||||||
|
- Don't raise KeyError when SystemLibraryPath is absent [`1fd0f96`](https://github.com/RhetTbull/osxphotos/commit/1fd0f96b14f0bc38e47bddb4cae12e19406324fb)
|
||||||
|
|
||||||
#### [v0.29.19](https://github.com/RhetTbull/osxphotos/compare/v0.29.18...v0.29.19)
|
#### [v0.29.19](https://github.com/RhetTbull/osxphotos/compare/v0.29.18...v0.29.19)
|
||||||
|
|
||||||
> 14 June 2020
|
> 14 June 2020
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.29.22"
|
__version__ = "0.29.26"
|
||||||
|
|||||||
@@ -21,12 +21,16 @@ import yaml
|
|||||||
from .._constants import (
|
from .._constants import (
|
||||||
_MOVIE_TYPE,
|
_MOVIE_TYPE,
|
||||||
_PHOTO_TYPE,
|
_PHOTO_TYPE,
|
||||||
|
_PHOTOS_4_ALBUM_KIND,
|
||||||
|
_PHOTOS_4_ROOT_FOLDER,
|
||||||
_PHOTOS_4_VERSION,
|
_PHOTOS_4_VERSION,
|
||||||
|
_PHOTOS_5_ALBUM_KIND,
|
||||||
|
_PHOTOS_5_SHARED_ALBUM_KIND,
|
||||||
_PHOTOS_5_SHARED_PHOTO_PATH,
|
_PHOTOS_5_SHARED_PHOTO_PATH,
|
||||||
)
|
)
|
||||||
from ..albuminfo import AlbumInfo
|
from ..albuminfo import AlbumInfo
|
||||||
from ..placeinfo import PlaceInfo4, PlaceInfo5
|
|
||||||
from ..phototemplate import PhotoTemplate
|
from ..phototemplate import PhotoTemplate
|
||||||
|
from ..placeinfo import PlaceInfo4, PlaceInfo5
|
||||||
from ..utils import _debug, _get_resource_loc, findfiles, get_preferred_uti_extension
|
from ..utils import _debug, _get_resource_loc, findfiles, get_preferred_uti_extension
|
||||||
|
|
||||||
|
|
||||||
@@ -341,21 +345,26 @@ class PhotoInfo:
|
|||||||
@property
|
@property
|
||||||
def albums(self):
|
def albums(self):
|
||||||
""" list of albums picture is contained in """
|
""" list of albums picture is contained in """
|
||||||
albums = []
|
try:
|
||||||
for album in self._info["albums"]:
|
return self._albums
|
||||||
if not self._db._dbalbum_details[album]["intrash"]:
|
except AttributeError:
|
||||||
albums.append(self._db._dbalbum_details[album]["title"])
|
album_uuids = self._get_album_uuids()
|
||||||
return albums
|
self._albums = list(
|
||||||
|
{self._db._dbalbum_details[album]["title"] for album in album_uuids}
|
||||||
|
)
|
||||||
|
return self._albums
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def album_info(self):
|
def album_info(self):
|
||||||
""" list of AlbumInfo objects representing albums the photos is contained in """
|
""" list of AlbumInfo objects representing albums the photos is contained in """
|
||||||
albums = []
|
try:
|
||||||
for album in self._info["albums"]:
|
return self._album_info
|
||||||
if not self._db._dbalbum_details[album]["intrash"]:
|
except AttributeError:
|
||||||
albums.append(AlbumInfo(db=self._db, uuid=album))
|
album_uuids = self._get_album_uuids()
|
||||||
|
self._album_info = [
|
||||||
return albums
|
AlbumInfo(db=self._db, uuid=album) for album in album_uuids
|
||||||
|
]
|
||||||
|
return self._album_info
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keywords(self):
|
def keywords(self):
|
||||||
@@ -655,6 +664,37 @@ class PhotoInfo:
|
|||||||
""" Returns latitude, in degrees """
|
""" Returns latitude, in degrees """
|
||||||
return self._info["latitude"]
|
return self._info["latitude"]
|
||||||
|
|
||||||
|
def _get_album_uuids(self):
|
||||||
|
""" Return list of album UUIDs this photo is found in
|
||||||
|
|
||||||
|
Filters out albums in the trash and any special album types
|
||||||
|
|
||||||
|
Returns: list of album UUIDs
|
||||||
|
"""
|
||||||
|
if self._db._db_version <= _PHOTOS_4_VERSION:
|
||||||
|
version4 = True
|
||||||
|
album_kind = [_PHOTOS_4_ALBUM_KIND]
|
||||||
|
else:
|
||||||
|
version4 = False
|
||||||
|
album_kind = [_PHOTOS_5_SHARED_ALBUM_KIND, _PHOTOS_5_ALBUM_KIND]
|
||||||
|
|
||||||
|
album_list = []
|
||||||
|
for album in self._info["albums"]:
|
||||||
|
detail = self._db._dbalbum_details[album]
|
||||||
|
if (
|
||||||
|
detail["kind"] in album_kind
|
||||||
|
and not detail["intrash"]
|
||||||
|
and (
|
||||||
|
not version4
|
||||||
|
# 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
|
||||||
|
or (version4 and detail["folderUuid"] != _PHOTOS_4_ROOT_FOLDER)
|
||||||
|
)
|
||||||
|
):
|
||||||
|
album_list.append(album)
|
||||||
|
return album_list
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"osxphotos.{self.__class__.__name__}(db={self._db}, uuid='{self._uuid}', info={self._info})"
|
return f"osxphotos.{self.__class__.__name__}(db={self._db}, uuid='{self._uuid}', info={self._info})"
|
||||||
|
|
||||||
@@ -772,12 +812,18 @@ class PhotoInfo:
|
|||||||
}
|
}
|
||||||
return json.dumps(pic)
|
return json.dumps(pic)
|
||||||
|
|
||||||
# compare two PhotoInfo objects for equality
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
""" Compare two PhotoInfo objects for equality """
|
||||||
|
# Can't just compare the two __dicts__ because some methods (like albums)
|
||||||
|
# memoize their value once called in an instance variable (e.g. self._albums)
|
||||||
if isinstance(other, self.__class__):
|
if isinstance(other, self.__class__):
|
||||||
return self.__dict__ == other.__dict__
|
return (
|
||||||
|
self._db.db_path == other._db.db_path
|
||||||
|
and self.uuid == other.uuid
|
||||||
|
and self._info == other._info
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
|
""" Compare two PhotoInfo objects for inequality """
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|||||||
@@ -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 UUIDs
|
||||||
|
"""
|
||||||
|
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 (
|
||||||
|
not version4
|
||||||
|
# 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
|
||||||
|
or (version4 and detail["folderUuid"] != _PHOTOS_4_ROOT_FOLDER)
|
||||||
|
)
|
||||||
|
):
|
||||||
|
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,
|
||||||
|
|||||||
@@ -503,7 +503,6 @@ class PlaceInfo5(PlaceInfo):
|
|||||||
""" revgeoloc_bplist: a binary plist blob containing
|
""" revgeoloc_bplist: a binary plist blob containing
|
||||||
a serialized PLRevGeoLocationInfo object """
|
a serialized PLRevGeoLocationInfo object """
|
||||||
self._bplist = revgeoloc_bplist
|
self._bplist = revgeoloc_bplist
|
||||||
# todo: check for None?
|
|
||||||
self._plrevgeoloc = archiver.unarchive(revgeoloc_bplist)
|
self._plrevgeoloc = archiver.unarchive(revgeoloc_bplist)
|
||||||
self._process_place_info()
|
self._process_place_info()
|
||||||
|
|
||||||
@@ -535,16 +534,21 @@ class PlaceInfo5(PlaceInfo):
|
|||||||
@property
|
@property
|
||||||
def address(self):
|
def address(self):
|
||||||
addr = self._plrevgeoloc.postalAddress
|
addr = self._plrevgeoloc.postalAddress
|
||||||
return PostalAddress(
|
if addr is not None:
|
||||||
street=addr._street,
|
postal_address = PostalAddress(
|
||||||
sub_locality=addr._subLocality,
|
street=addr._street,
|
||||||
city=addr._city,
|
sub_locality=addr._subLocality,
|
||||||
sub_administrative_area=addr._subAdministrativeArea,
|
city=addr._city,
|
||||||
state_province=addr._state,
|
sub_administrative_area=addr._subAdministrativeArea,
|
||||||
postal_code=addr._postalCode,
|
state_province=addr._state,
|
||||||
country=addr._country,
|
postal_code=addr._postalCode,
|
||||||
iso_country_code=addr._ISOCountryCode,
|
country=addr._country,
|
||||||
)
|
iso_country_code=addr._ISOCountryCode,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
postal_address = None
|
||||||
|
|
||||||
|
return postal_address
|
||||||
|
|
||||||
def _process_place_info(self):
|
def _process_place_info(self):
|
||||||
""" Process sortedPlaceInfos to set self._name and self._names """
|
""" Process sortedPlaceInfos to set self._name and self._names """
|
||||||
@@ -632,5 +636,5 @@ class PlaceInfo5(PlaceInfo):
|
|||||||
"country_code": self.country_code,
|
"country_code": self.country_code,
|
||||||
"ishome": self.ishome,
|
"ishome": self.ishome,
|
||||||
"address_str": self.address_str,
|
"address_str": self.address_str,
|
||||||
"address": self.address._asdict(),
|
"address": self.address._asdict() if self.address is not None else 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():
|
||||||
@@ -124,7 +124,7 @@ def test_attributes():
|
|||||||
)
|
)
|
||||||
assert p.description == "Girl holding pumpkin"
|
assert p.description == "Girl holding pumpkin"
|
||||||
assert p.title == "I found one!"
|
assert p.title == "I found one!"
|
||||||
assert p.albums == ["Pumpkin Farm", "AlbumInFolder"]
|
assert sorted(p.albums) == ["AlbumInFolder", "Pumpkin Farm"]
|
||||||
assert p.persons == ["Katie"]
|
assert p.persons == ["Katie"]
|
||||||
assert p.path.endswith(
|
assert p.path.endswith(
|
||||||
"/tests/Test-10.12.6.photoslibrary/Masters/2019/08/24/20190824-030824/Pumkins2.jpg"
|
"/tests/Test-10.12.6.photoslibrary/Masters/2019/08/24/20190824-030824/Pumkins2.jpg"
|
||||||
|
|||||||
@@ -229,6 +229,7 @@ def test_albums_photos():
|
|||||||
|
|
||||||
|
|
||||||
def test_photoinfo_albums():
|
def test_photoinfo_albums():
|
||||||
|
""" Test PhotoInfo.albums """
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
@@ -238,7 +239,20 @@ def test_photoinfo_albums():
|
|||||||
assert "Pumpkin Farm" in albums
|
assert "Pumpkin Farm" in albums
|
||||||
|
|
||||||
|
|
||||||
|
def test_photoinfo_albums_2():
|
||||||
|
""" Test that PhotoInfo.albums returns only number albums expected """
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
|
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
|
photos = photosdb.photos(uuid=[UUID_DICT["two_albums"]])
|
||||||
|
|
||||||
|
albums = photos[0].albums
|
||||||
|
assert len(albums) == 2
|
||||||
|
|
||||||
|
|
||||||
def test_photoinfo_album_info():
|
def test_photoinfo_album_info():
|
||||||
|
""" test PhotoInfo.album_info """
|
||||||
|
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
@@ -249,4 +263,4 @@ def test_photoinfo_album_info():
|
|||||||
assert album_info[0].title in ["Pumpkin Farm", "Test Album"]
|
assert album_info[0].title in ["Pumpkin Farm", "Test Album"]
|
||||||
assert album_info[1].title in ["Pumpkin Farm", "Test Album"]
|
assert album_info[1].title in ["Pumpkin Farm", "Test Album"]
|
||||||
|
|
||||||
assert photos[0] in album_info[0].photos
|
assert photos[0].uuid in [photo.uuid for photo in album_info[0].photos]
|
||||||
|
|||||||
@@ -244,4 +244,4 @@ def test_photoinfo_album_info():
|
|||||||
assert album_info[0].title in ["Pumpkin Farm", "Test Album"]
|
assert album_info[0].title in ["Pumpkin Farm", "Test Album"]
|
||||||
assert album_info[1].title in ["Pumpkin Farm", "Test Album"]
|
assert album_info[1].title in ["Pumpkin Farm", "Test Album"]
|
||||||
|
|
||||||
assert photos[0] in album_info[0].photos
|
assert photos[0].uuid in [photo.uuid for photo in album_info[0].photos]
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ def test_init4():
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_init5(mocker):
|
def test_init5(mocker):
|
||||||
# test failed get_last_library_path
|
# test failed get_last_library_path
|
||||||
import osxphotos
|
import osxphotos
|
||||||
@@ -117,7 +118,6 @@ def test_init5(mocker):
|
|||||||
# because of the layout of photosdb/ need to patch it this way...don't really understand why, but it works
|
# because of the layout of photosdb/ need to patch it this way...don't really understand why, but it works
|
||||||
mocker.patch("osxphotos.photosdb.photosdb.get_last_library_path", new=bad_library)
|
mocker.patch("osxphotos.photosdb.photosdb.get_last_library_path", new=bad_library)
|
||||||
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
assert osxphotos.PhotosDB()
|
assert osxphotos.PhotosDB()
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ def test_attributes():
|
|||||||
)
|
)
|
||||||
assert p.description == "Girl holding pumpkin"
|
assert p.description == "Girl holding pumpkin"
|
||||||
assert p.title == "I found one!"
|
assert p.title == "I found one!"
|
||||||
assert p.albums == ["Pumpkin Farm", "Test Album", "Multi Keyword"]
|
assert sorted(p.albums) == ["Multi Keyword", "Pumpkin Farm", "Test Album"]
|
||||||
assert p.persons == ["Katie"]
|
assert p.persons == ["Katie"]
|
||||||
assert p.path.endswith(
|
assert p.path.endswith(
|
||||||
"tests/Test-10.15.1.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
"tests/Test-10.15.1.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ def test_attributes():
|
|||||||
)
|
)
|
||||||
assert p.description == "Girl holding pumpkin"
|
assert p.description == "Girl holding pumpkin"
|
||||||
assert p.title == "I found one!"
|
assert p.title == "I found one!"
|
||||||
assert p.albums == ["Pumpkin Farm", "Test Album"]
|
assert sorted(p.albums) == ["Pumpkin Farm", "Test Album"]
|
||||||
assert p.persons == ["Katie"]
|
assert p.persons == ["Katie"]
|
||||||
assert p.path.endswith(
|
assert p.path.endswith(
|
||||||
"tests/Test-10.15.4.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
"tests/Test-10.15.4.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
||||||
|
|||||||
@@ -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 = {
|
||||||
@@ -63,6 +65,7 @@ UUID_DICT = {
|
|||||||
"no_external_edit": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
"no_external_edit": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
|
||||||
"export": "D79B8D77-BFFC-460B-9312-034F2877D35B", # "Pumkins2.jpg"
|
"export": "D79B8D77-BFFC-460B-9312-034F2877D35B", # "Pumkins2.jpg"
|
||||||
"export_tif": "8846E3E6-8AC8-4857-8448-E3D025784410",
|
"export_tif": "8846E3E6-8AC8-4857-8448-E3D025784410",
|
||||||
|
"in_album": "D79B8D77-BFFC-460B-9312-034F2877D35B", # "Pumkins2.jpg"
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID_PUMPKIN_FARM = [
|
UUID_PUMPKIN_FARM = [
|
||||||
@@ -225,7 +228,7 @@ def test_attributes():
|
|||||||
)
|
)
|
||||||
assert p.description == "Girl holding pumpkin"
|
assert p.description == "Girl holding pumpkin"
|
||||||
assert p.title == "I found one!"
|
assert p.title == "I found one!"
|
||||||
assert p.albums == ["Pumpkin Farm", "Test Album"]
|
assert sorted(p.albums) == ["Pumpkin Farm", "Test Album"]
|
||||||
assert p.persons == ["Katie"]
|
assert p.persons == ["Katie"]
|
||||||
assert p.path.endswith(
|
assert p.path.endswith(
|
||||||
"tests/Test-10.15.5.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
"tests/Test-10.15.5.photoslibrary/originals/D/D79B8D77-BFFC-460B-9312-034F2877D35B.jpeg"
|
||||||
@@ -797,11 +800,29 @@ def test_export_14(caplog):
|
|||||||
|
|
||||||
|
|
||||||
def test_eq():
|
def test_eq():
|
||||||
|
""" Test equality of two PhotoInfo objects """
|
||||||
import osxphotos
|
import osxphotos
|
||||||
|
|
||||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
photosdb1 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
photos1 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
photosdb2 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
photos2 = photosdb.photos(uuid=[UUID_DICT["export"]])
|
photos1 = photosdb1.photos(uuid=[UUID_DICT["export"]])
|
||||||
|
photos2 = photosdb2.photos(uuid=[UUID_DICT["export"]])
|
||||||
|
assert photos1[0] == photos2[0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_eq_2():
|
||||||
|
""" Test equality of two PhotoInfo objects when one has memoized property """
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
|
photosdb1 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
|
photosdb2 = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||||
|
photos1 = photosdb1.photos(uuid=[UUID_DICT["in_album"]])
|
||||||
|
photos2 = photosdb2.photos(uuid=[UUID_DICT["in_album"]])
|
||||||
|
|
||||||
|
# memoize a value
|
||||||
|
albums = photos1[0].albums
|
||||||
|
assert albums
|
||||||
|
|
||||||
assert photos1[0] == photos2[0]
|
assert photos1[0] == photos2[0]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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": {},
|
||||||
}
|
}
|
||||||
@@ -354,7 +355,10 @@ def test_query_uuid():
|
|||||||
for key_ in json_expected:
|
for key_ in json_expected:
|
||||||
assert key_ in json_got
|
assert key_ in json_got
|
||||||
if key_ != "path":
|
if key_ != "path":
|
||||||
assert json_expected[key_] == json_got[key_]
|
if isinstance(json_expected[key_], list):
|
||||||
|
assert sorted(json_expected[key_]) == sorted(json_got[key_])
|
||||||
|
else:
|
||||||
|
assert json_expected[key_] == json_got[key_]
|
||||||
else:
|
else:
|
||||||
assert json_expected[key_] in json_got[key_]
|
assert json_expected[key_] in json_got[key_]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user