Feature person favorite 940 (#950)
* Added person favorite for #940 * Added person.feature_less #940 (but not 100% sure it's right yet) * Added tests for PersonInfo.favorite, PersonInfo.feature_less
This commit is contained in:
@@ -3,8 +3,10 @@
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
|
||||
from collections import namedtuple
|
||||
from functools import cached_property
|
||||
|
||||
import osxphotos
|
||||
|
||||
__all__ = ["PersonInfo", "FaceInfo", "rotate_image_point"]
|
||||
|
||||
@@ -15,7 +17,7 @@ MPRI_Reg_Rect = namedtuple("MPRI_Reg_Rect", ["x", "y", "h", "w"])
|
||||
class PersonInfo:
|
||||
"""Info about a person in the Photos library"""
|
||||
|
||||
def __init__(self, db=None, pk=None):
|
||||
def __init__(self, db: "osxphotos.PhotosDB", pk: int):
|
||||
"""Creates a new PersonInfo instance
|
||||
|
||||
Arguments:
|
||||
@@ -25,8 +27,8 @@ class PersonInfo:
|
||||
Returns:
|
||||
PersonInfo instance
|
||||
"""
|
||||
self._db = db
|
||||
self._pk = pk
|
||||
self._db: "osxphotos.PhotosDB" = db
|
||||
self._pk: int = pk
|
||||
|
||||
person = self._db._dbpersons_pk[pk]
|
||||
self.uuid = person["uuid"]
|
||||
@@ -72,6 +74,33 @@ class PersonInfo:
|
||||
# no faces
|
||||
return []
|
||||
|
||||
@property
|
||||
def favorite(self):
|
||||
"""Returns True if person is a favorite, False otherwise; Photos 5+ only; returns False on Photos <= 4"""
|
||||
return self._db._dbpersons_pk[self._pk]["type"] == 1
|
||||
|
||||
@property
|
||||
def sort_order(self):
|
||||
"""Returns sort order of person; favorite persons are sorted before non-favorite persons"; Photos 5+ only; returns 0 on Photos <= 4"""
|
||||
return self._db._dbpersons_pk[self._pk]["manualorder"]
|
||||
|
||||
@cached_property
|
||||
def feature_less(self):
|
||||
"""Returns True if person has been marked as "Feature This Person Less" in Photos, False otherwise; Photos 8+ only; returns False on Photos <= 7"""
|
||||
if self._db.photos_version < 8:
|
||||
return False
|
||||
|
||||
if results := self._db.execute(
|
||||
"""
|
||||
SELECT ZTYPE
|
||||
FROM ZUSERFEEDBACK
|
||||
WHERE ZPERSON = ?
|
||||
""",
|
||||
(self._pk,),
|
||||
).fetchone():
|
||||
return bool(results[0])
|
||||
return False
|
||||
|
||||
def asdict(self):
|
||||
"""Returns dictionary representation of class instance"""
|
||||
keyphoto = self.keyphoto.uuid if self.keyphoto is not None else None
|
||||
@@ -82,6 +111,9 @@ class PersonInfo:
|
||||
"keyface": self.keyface,
|
||||
"facecount": self.facecount,
|
||||
"keyphoto": keyphoto,
|
||||
"favorite": self.favorite,
|
||||
"sort_order": self.sort_order,
|
||||
"feature_less": self.feature_less,
|
||||
}
|
||||
|
||||
def json(self):
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
"""
|
||||
PhotoInfo class
|
||||
Represents a single photo in the Photos library and provides access to the photo's attributes
|
||||
""" PhotoInfo class: Represents a single photo in the Photos library and provides access to the photo's attributes
|
||||
PhotosDB.photos() returns a list of PhotoInfo objects
|
||||
"""
|
||||
|
||||
@@ -17,11 +15,13 @@ import pathlib
|
||||
import plistlib
|
||||
from datetime import timedelta, timezone
|
||||
from functools import cached_property
|
||||
from typing import Dict, Optional
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import yaml
|
||||
from osxmetadata import OSXMetaData
|
||||
|
||||
import osxphotos
|
||||
|
||||
from ._constants import (
|
||||
_DB_TABLE_NAMES,
|
||||
_MOVIE_TYPE,
|
||||
@@ -75,10 +75,10 @@ class PhotoInfo:
|
||||
including keywords, persons, albums, uuid, path, etc.
|
||||
"""
|
||||
|
||||
def __init__(self, db=None, uuid=None, info=None):
|
||||
self._uuid = uuid
|
||||
self._info = info
|
||||
self._db = db
|
||||
def __init__(self, db: "osxphotos.PhotosDB", uuid: str, info: dict[str, Any]):
|
||||
self._uuid: str = uuid
|
||||
self._info: dict[str, Any] = info
|
||||
self._db: "osxphotos.PhotosDB" = db
|
||||
self._verbose = self._db._verbose
|
||||
|
||||
@property
|
||||
|
||||
@@ -3,18 +3,21 @@ PhotosDB class
|
||||
Processes a Photos.app library database to extract information about photos
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import platform
|
||||
import re
|
||||
import sqlite3
|
||||
import sys
|
||||
import tempfile
|
||||
from collections import OrderedDict
|
||||
from collections.abc import Iterable
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import List, Optional
|
||||
from typing import Any, List, Optional
|
||||
from unicodedata import normalize
|
||||
|
||||
import bitmath
|
||||
@@ -705,6 +708,8 @@ class PhotosDB:
|
||||
"displayname": normalize_unicode(person[4]),
|
||||
"photo_uuid": None,
|
||||
"keyface_uuid": None,
|
||||
"type": None, # Photos 5+
|
||||
"manualorder": 0, # Photos 5+
|
||||
}
|
||||
try:
|
||||
self._dbpersons_fullname[fullname].append(pk)
|
||||
@@ -1687,7 +1692,9 @@ class PhotosDB:
|
||||
ZPERSON.ZFULLNAME,
|
||||
ZPERSON.ZFACECOUNT,
|
||||
ZPERSON.ZKEYFACE,
|
||||
ZPERSON.ZDISPLAYNAME
|
||||
ZPERSON.ZDISPLAYNAME,
|
||||
ZPERSON.ZTYPE,
|
||||
ZPERSON.ZMANUALORDER
|
||||
FROM ZPERSON
|
||||
"""
|
||||
)
|
||||
@@ -1698,6 +1705,8 @@ class PhotosDB:
|
||||
# 3 ZPERSON.ZFACECOUNT,
|
||||
# 4 ZPERSON.ZKEYFACE,
|
||||
# 5 ZPERSON.ZDISPLAYNAME
|
||||
# 6 ZPERSON.ZTYPE, # ZTYPE = 1 == favorite, 0 == not favorite
|
||||
# 7 ZPERSON.ZMANUALORDER # favorites are sorted by ZMANUALORDER
|
||||
|
||||
for person in c:
|
||||
pk = person[0]
|
||||
@@ -1715,6 +1724,8 @@ class PhotosDB:
|
||||
"displayname": normalize_unicode(person[5]),
|
||||
"photo_uuid": None,
|
||||
"keyface_uuid": None,
|
||||
"type": person[6],
|
||||
"manualorder": person[7],
|
||||
}
|
||||
try:
|
||||
self._dbpersons_fullname[fullname].append(pk)
|
||||
@@ -3563,10 +3574,11 @@ class PhotosDB:
|
||||
|
||||
return photos
|
||||
|
||||
def execute(self, sql):
|
||||
def execute(self, sql: str, params: Any | None = None) -> sqlite3.Cursor:
|
||||
"""Execute sql statement and return cursor"""
|
||||
self._db_connection, _ = self.get_db_connection()
|
||||
return self._db_connection.cursor().execute(sql)
|
||||
params = params or ()
|
||||
return self._db_connection.cursor().execute(sql, params)
|
||||
|
||||
def _duplicate_signature(self, uuid):
|
||||
"""Compute a signature for finding possible duplicates"""
|
||||
|
||||
Reference in New Issue
Block a user