Added compare for PhotoInfo objects, added loguru
This commit is contained in:
@@ -17,7 +17,8 @@ from . import _applescript
|
|||||||
|
|
||||||
# replace string formatting with fstrings
|
# replace string formatting with fstrings
|
||||||
|
|
||||||
_debug = True
|
_debug = False
|
||||||
|
|
||||||
|
|
||||||
def _get_os_version():
|
def _get_os_version():
|
||||||
# returns tuple containing OS version
|
# returns tuple containing OS version
|
||||||
@@ -55,10 +56,10 @@ class PhotosDB:
|
|||||||
print(dbfile)
|
print(dbfile)
|
||||||
if dbfile is None:
|
if dbfile is None:
|
||||||
library_path = self.get_photos_library_path()
|
library_path = self.get_photos_library_path()
|
||||||
print("library_path: " + library_path)
|
logger.debug("library_path: " + library_path)
|
||||||
# TODO: verify library path not None
|
# TODO: verify library path not None
|
||||||
dbfile = os.path.join(library_path, "database/photos.db")
|
dbfile = os.path.join(library_path, "database/photos.db")
|
||||||
print(dbfile)
|
logger.debug(dbfile)
|
||||||
|
|
||||||
logger.debug("filename = %s" % dbfile)
|
logger.debug("filename = %s" % dbfile)
|
||||||
|
|
||||||
@@ -93,15 +94,15 @@ class PhotosDB:
|
|||||||
for k in self._dbfaces_person.keys():
|
for k in self._dbfaces_person.keys():
|
||||||
persons[k] = len(self._dbfaces_person[k])
|
persons[k] = len(self._dbfaces_person[k])
|
||||||
persons = dict(sorted(persons.items(), key=lambda kv: kv[1], reverse=True))
|
persons = dict(sorted(persons.items(), key=lambda kv: kv[1], reverse=True))
|
||||||
return persons
|
return persons
|
||||||
|
|
||||||
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 = {}
|
||||||
for k in self._dbalbums_album.keys():
|
for k in self._dbalbums_album.keys():
|
||||||
albums[k] = len(self._dbalbums_album[k])
|
albums[k] = len(self._dbalbums_album[k])
|
||||||
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
|
||||||
|
|
||||||
def keywords(self):
|
def keywords(self):
|
||||||
# return list of keywords found in photos database
|
# return list of keywords found in photos database
|
||||||
@@ -115,10 +116,9 @@ class PhotosDB:
|
|||||||
|
|
||||||
def albums(self):
|
def albums(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= self._dbalbums_album.keys()
|
albums = self._dbalbums_album.keys()
|
||||||
return list(albums)
|
return list(albums)
|
||||||
|
|
||||||
|
|
||||||
# Various AppleScripts we need
|
# Various AppleScripts we need
|
||||||
def _setup_applescript(self):
|
def _setup_applescript(self):
|
||||||
self._scpt_export = ""
|
self._scpt_export = ""
|
||||||
@@ -252,7 +252,8 @@ class PhotosDB:
|
|||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
c.execute(
|
c.execute(
|
||||||
"select count(*) from RKFace, RKPerson where RKFace.personID = RKperson.modelID"
|
"select count(*) from RKFace, RKPerson, RKVersion where RKFace.personID = RKperson.modelID "
|
||||||
|
+ "and RKFace.imageModelId = RKVersion.modelId and RKVersion.isInTrash = 0"
|
||||||
)
|
)
|
||||||
# init_pbar_status("Faces", c.fetchone()[0])
|
# init_pbar_status("Faces", c.fetchone()[0])
|
||||||
# c.execute("select RKPerson.name, RKFace.imageID from RKFace, RKPerson where RKFace.personID = RKperson.modelID")
|
# c.execute("select RKPerson.name, RKFace.imageID from RKFace, RKPerson where RKFace.personID = RKperson.modelID")
|
||||||
@@ -260,7 +261,7 @@ class PhotosDB:
|
|||||||
"select RKPerson.name, RKVersion.uuid from RKFace, RKPerson, RKVersion, RKMaster "
|
"select RKPerson.name, RKVersion.uuid from RKFace, RKPerson, RKVersion, RKMaster "
|
||||||
+ "where RKFace.personID = RKperson.modelID and RKVersion.modelId = RKFace.ImageModelId "
|
+ "where RKFace.personID = RKperson.modelID and RKVersion.modelId = RKFace.ImageModelId "
|
||||||
+ "and RKVersion.type = 2 and RKVersion.masterUuid = RKMaster.uuid and "
|
+ "and RKVersion.type = 2 and RKVersion.masterUuid = RKMaster.uuid and "
|
||||||
+ "RKVersion.filename not like '%.pdf'"
|
+ "RKVersion.filename not like '%.pdf' and RKVersion.isInTrash = 0"
|
||||||
)
|
)
|
||||||
for person in c:
|
for person in c:
|
||||||
if person[0] == None:
|
if person[0] == None:
|
||||||
@@ -472,74 +473,87 @@ class PhotosDB:
|
|||||||
|
|
||||||
logger.debug(f"processed {len(self._dbphotos)} photos")
|
logger.debug(f"processed {len(self._dbphotos)} photos")
|
||||||
|
|
||||||
def photos(self, keywords = [],uuid=[],persons=[],albums=[]):
|
"""
|
||||||
|
Return a list of PhotoInfo objects
|
||||||
|
If called with no args, returns the entire database of photos
|
||||||
|
If called with args, returns photos matching the args (e.g. keywords, persons, etc.)
|
||||||
|
If more than one arg, returns photos matching all the criteria (e.g. keywords AND persons)
|
||||||
|
"""
|
||||||
|
def photos(self, keywords=[], uuid=[], persons=[], albums=[]):
|
||||||
photos = []
|
photos = []
|
||||||
if not keywords and not uuid and not persons and not albums:
|
if not keywords and not uuid and not persons and not albums:
|
||||||
#process all the photos
|
# process all the photos
|
||||||
photos = list(self._dbphotos.keys())
|
photos = list(self._dbphotos.keys())
|
||||||
else:
|
else:
|
||||||
if albums is not None:
|
if albums is not None:
|
||||||
for album in albums:
|
for album in albums:
|
||||||
print("album=%s" % album)
|
logger.info("album=%s" % album)
|
||||||
if album in self._dbalbums_album:
|
if album in self._dbalbums_album:
|
||||||
print("processing album %s:" % album)
|
logger.info("processing album %s:" % album)
|
||||||
photos.extend(self._dbalbums_album[album])
|
photos.extend(self._dbalbums_album[album])
|
||||||
else:
|
else:
|
||||||
print("Could not find album '%s' in database" %
|
logger.debug(
|
||||||
(album), file=sys.stderr)
|
"Could not find album '%s' in database" % (album),
|
||||||
|
)
|
||||||
|
|
||||||
if uuid is not None:
|
if uuid is not None:
|
||||||
for u in uuid:
|
for u in uuid:
|
||||||
print("uuid=%s" % u)
|
logger.info("uuid=%s" % u)
|
||||||
if u in self._dbphotos:
|
if u in self._dbphotos:
|
||||||
print("processing uuid %s:" % u)
|
logger.info("processing uuid %s:" % u)
|
||||||
photos.extend([u])
|
photos.extend([u])
|
||||||
else:
|
else:
|
||||||
print("Could not find uuid '%s' in database" %
|
logger.debug(
|
||||||
(u), file=sys.stderr)
|
"Could not find uuid '%s' in database" % (u),
|
||||||
|
)
|
||||||
|
|
||||||
if keywords is not None:
|
if keywords is not None:
|
||||||
for keyword in keywords:
|
for keyword in keywords:
|
||||||
print("keyword=%s" % keyword)
|
logger.info("keyword=%s" % keyword)
|
||||||
if keyword in self._dbkeywords_keyword:
|
if keyword in self._dbkeywords_keyword:
|
||||||
print("processing keyword %s:" % keyword)
|
logger.info("processing keyword %s:" % keyword)
|
||||||
photos.extend(self._dbkeywords_keyword[keyword])
|
photos.extend(self._dbkeywords_keyword[keyword])
|
||||||
else:
|
else:
|
||||||
print("Could not find keyword '%s' in database" %
|
logger.debug(
|
||||||
(keyword), file=sys.stderr)
|
"Could not find keyword '%s' in database" % (keyword),
|
||||||
|
)
|
||||||
|
|
||||||
if persons is not None:
|
if persons is not None:
|
||||||
for person in persons:
|
for person in persons:
|
||||||
print("person=%s" % person)
|
logger.info("person=%s" % person)
|
||||||
if person in self._dbfaces_person:
|
if person in self._dbfaces_person:
|
||||||
print("processing person %s:" % person)
|
logger.info("processing person %s:" % person)
|
||||||
photos.extend(self._dbfaces_person[person])
|
photos.extend(self._dbfaces_person[person])
|
||||||
else:
|
else:
|
||||||
print("Could not find person '%s' in database" %
|
logger.debug(
|
||||||
(person), file=sys.stderr)
|
"Could not find person '%s' in database" % (person),
|
||||||
|
)
|
||||||
|
|
||||||
photoinfo = []
|
photoinfo = []
|
||||||
for p in photos:
|
for p in photos:
|
||||||
info = PhotoInfo(db = self, uuid = p, info = self._dbphotos[p])
|
logger.info(f"p={p}")
|
||||||
|
info = PhotoInfo(db=self, uuid=p, info=self._dbphotos[p])
|
||||||
photoinfo.append(info)
|
photoinfo.append(info)
|
||||||
return photoinfo
|
return photoinfo
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Info about a specific photo, contains all the details we know about the photo
|
Info about a specific photo, contains all the details we know about the photo
|
||||||
including keywords, persons, albums, uuid, path, etc.
|
including keywords, persons, albums, uuid, path, etc.
|
||||||
"""
|
"""
|
||||||
class PhotoInfo():
|
|
||||||
def __init__(self, db = None, uuid = None, info = None):
|
|
||||||
|
class PhotoInfo:
|
||||||
|
def __init__(self, db=None, uuid=None, info=None):
|
||||||
self.uuid = uuid
|
self.uuid = uuid
|
||||||
self.info = info
|
self.info = info
|
||||||
self.db = db
|
self.db = db
|
||||||
|
|
||||||
def filename(self):
|
def filename(self):
|
||||||
return self.info['filename']
|
return self.info["filename"]
|
||||||
|
|
||||||
def date(self):
|
def date(self):
|
||||||
return self.info['imageDate']
|
return self.info["imageDate"]
|
||||||
|
|
||||||
""" returns true if photo is missing from disk (which means it's not been downloaded from iCloud)
|
""" returns true if photo is missing from disk (which means it's not been downloaded from iCloud)
|
||||||
NOTE: the photos.db database uses an asynchrounous write-ahead log so changes in Photos
|
NOTE: the photos.db database uses an asynchrounous write-ahead log so changes in Photos
|
||||||
@@ -550,39 +564,50 @@ class PhotoInfo():
|
|||||||
downloaded from cloud to local storate their status in the database might still show
|
downloaded from cloud to local storate their status in the database might still show
|
||||||
isMissing = 1
|
isMissing = 1
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def ismissing(self):
|
def ismissing(self):
|
||||||
return self.info['isMissing']
|
return self.info["isMissing"]
|
||||||
|
|
||||||
def path(self):
|
def path(self):
|
||||||
photopath = ""
|
photopath = ""
|
||||||
|
|
||||||
vol = self.info['volume']
|
vol = self.info["volume"]
|
||||||
if vol is not None:
|
if vol is not None:
|
||||||
photopath = os.path.join('/Volumes', vol, self.info['imagePath'])
|
photopath = os.path.join("/Volumes", vol, self.info["imagePath"])
|
||||||
else:
|
else:
|
||||||
photopath = os.path.join(self.db._masters_path, self.info['imagePath'])
|
photopath = os.path.join(self.db._masters_path, self.info["imagePath"])
|
||||||
|
|
||||||
if self.info['isMissing'] == 1:
|
if self.info["isMissing"] == 1:
|
||||||
logger.warning(f"Skipping photo, not yet downloaded from iCloud: {photopath}")
|
logger.warning(
|
||||||
|
f"Skipping photo, not yet downloaded from iCloud: {photopath}"
|
||||||
|
)
|
||||||
print(self.info)
|
print(self.info)
|
||||||
photopath = None #path would be meaningless until downloaded
|
photopath = None # path would be meaningless until downloaded
|
||||||
#TODO: Is there a way to use applescript to force the download in this
|
# TODO: Is there a way to use applescript to force the download in this
|
||||||
|
|
||||||
return photopath
|
return photopath
|
||||||
|
|
||||||
def description(self):
|
def description(self):
|
||||||
return self.info['extendedDescription']
|
return self.info["extendedDescription"]
|
||||||
|
|
||||||
def persons(self):
|
def persons(self):
|
||||||
return self.info['persons']
|
return self.info["persons"]
|
||||||
|
|
||||||
def albums(self):
|
def albums(self):
|
||||||
return self.info['albums']
|
return self.info["albums"]
|
||||||
|
|
||||||
def keywords(self):
|
def keywords(self):
|
||||||
return self.info['keywords']
|
return self.info["keywords"]
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
return self.info['name']
|
return self.info["name"]
|
||||||
|
|
||||||
|
|
||||||
|
# compare two PhotoInfo objects for equality
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
return self.__dict__ == other.__dict__
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|||||||
Reference in New Issue
Block a user