Source code for osxphotos.searchinfo

"""class for PhotoInfo exposing SearchInfo data such as labels"""

from ._constants import _PHOTOS_4_VERSION, search_category_factory

__all__ = ["SearchInfo"]


[docs] class SearchInfo: """Info about search terms such as machine learning labels that Photos knows about a photo""" def __init__(self, photo, normalized=False): """photo: PhotoInfo object normalized: if True, all properties return normalized (lower case) results""" if photo._db._db_version <= _PHOTOS_4_VERSION: raise NotImplementedError( "search info not implemented for this database version" ) self._categories = search_category_factory(photo._db._photos_ver) self._photo = photo self._normalized = normalized self.uuid = photo.uuid try: # get search info for this UUID # there might not be any search info data (e.g. if Photo was missing or photoanalysisd not run yet) self._db_searchinfo = photo._db._db_searchinfo_uuid[self.uuid] except KeyError: self._db_searchinfo = None @property def labels(self): """return list of labels associated with Photo""" return self._get_text_for_category(self._categories.LABEL) @property def place_names(self): """returns list of place names""" return self._get_text_for_category(self._categories.PLACE_NAME) @property def streets(self): """returns list of street names""" return self._get_text_for_category(self._categories.STREET) @property def neighborhoods(self): """returns list of neighborhoods""" return self._get_text_for_category(self._categories.NEIGHBORHOOD) @property def locality_names(self): """returns list of other locality names""" locality = [] for category in self._categories.ALL_LOCALITY: locality += self._get_text_for_category(category) return locality @property def city(self): """returns city/town""" city = self._get_text_for_category(self._categories.CITY) return city[0] if city else "" @property def state(self): """returns state name""" state = self._get_text_for_category(self._categories.STATE) return state[0] if state else "" @property def state_abbreviation(self): """returns state abbreviation""" abbrev = self._get_text_for_category(self._categories.STATE_ABBREVIATION) return abbrev[0] if abbrev else "" @property def country(self): """returns country name""" country = self._get_text_for_category(self._categories.COUNTRY) return country[0] if country else "" @property def month(self): """returns month name""" month = self._get_text_for_category(self._categories.MONTH) return month[0] if month else "" @property def year(self): """returns year""" year = self._get_text_for_category(self._categories.YEAR) return year[0] if year else "" @property def bodies_of_water(self): """returns list of body of water names""" return self._get_text_for_category(self._categories.BODY_OF_WATER) @property def holidays(self): """returns list of holiday names""" return self._get_text_for_category(self._categories.HOLIDAY) @property def activities(self): """returns list of activity names""" return self._get_text_for_category(self._categories.ACTIVITY) @property def season(self): """returns season name""" season = self._get_text_for_category(self._categories.SEASON) return season[0] if season else "" @property def venues(self): """returns list of venue names""" return self._get_text_for_category(self._categories.VENUE) @property def venue_types(self): """returns list of venue types""" return self._get_text_for_category(self._categories.VENUE_TYPE) @property def media_types(self): """returns list of media types (photo, video, panorama, etc)""" types = [] for category in self._categories.MEDIA_TYPES: types += self._get_text_for_category(category) return types @property def detected_text(self): """Returns text detected in the photo (macOS 13+ / Photos 8+ only)""" if self._photo._db._photos_ver < 8: return [] return self._get_text_for_category(self._categories.DETECTED_TEXT) @property def text_found(self): """Returns True if photos has detected text (macOS 13+ / Photos 8+ only)""" if self._photo._db._photos_ver < 8: return [] return self._get_text_for_category(self._categories.TEXT_FOUND) @property def camera(self): """returns camera name (macOS 13+ / Photos 8+ only)""" if self._photo._db._photos_ver < 8: return "" camera = self._get_text_for_category(self._categories.CAMERA) return camera[0] if camera else "" @property def source(self): """returns source of the photo (e.g. "Messages", "Safar", etc) (macOS 13+ / Photos 8+ only)""" if self._photo._db._photos_ver < 8: return "" source = self._get_text_for_category(self._categories.SOURCE) return source[0] if source else "" @property def times_of_day(self): """returns list of times of day, e.g. "Morning" (Photos 8+ only)""" return self._get_text_for_category(self._categories.TIME_OF_DAY) @property def week_part(self): """returns part of week, "Weekday" or "Weekend" (Photos 8+ only)""" return self._get_first_text_for_category(self._categories.WEEKPART) @property def day_of_week(self): """returns day of week, e.g. "Monday" (macOS 27+ only)""" return self._get_first_text_for_category(self._categories.DAY_OF_WEEK) @property def home(self): """returns "Home" if photo was taken at the user's home location""" return self._get_first_text_for_category(self._categories.HOME) @property def areas_of_interest(self): """returns list of areas of interest, e.g. parks, airports, universities (Photos 8+ only)""" return self._get_text_for_category(self._categories.AREA_OF_INTEREST) @property def country_code(self): """returns country code, e.g. "US" (Photos 8+ only)""" return self._get_first_text_for_category(self._categories.COUNTRY_CODE) @property def region(self): """returns geographic region, e.g. "Northern America" (Photos 8+ only)""" return self._get_first_text_for_category(self._categories.REGION) @property def continent(self): """returns continent, e.g. "Americas" (Photos 8+ only)""" return self._get_first_text_for_category(self._categories.CONTINENT) @property def events(self): """returns list of event names, e.g. sporting events or concerts (Photos 8+ only)""" return self._get_text_for_category(self._categories.EVENT) @property def event_performers(self): """returns list of event performers or teams (Photos 8+ only)""" return self._get_text_for_category(self._categories.EVENT_PERFORMER) @property def event_types(self): """returns list of event types, e.g. "Football", "Music" (Photos 8+ only)""" return self._get_text_for_category(self._categories.EVENT_TYPE) @property def pets(self): """returns list of pet names (Photos 8+ only)""" return self._get_text_for_category(self._categories.PET) @property def age_groups(self): """returns list of age groups of people in the photo, e.g. "Adult", "Child" (macOS 27+ only)""" return self._get_text_for_category(self._categories.AGE_GROUP) @property def landmarks(self): """returns list of landmarks (Photos 8+ only)""" return self._get_text_for_category(self._categories.LANDMARK) @property def sounds(self): """returns list of sounds detected in video or Live Photo, e.g. "Laughter" (Photos 8+ only)""" return self._get_text_for_category(self._categories.SOUND) @property def human_actions(self): """returns list of human actions, e.g. "Dancing" (Photos 8+ only)""" return self._get_text_for_category(self._categories.HUMAN_ACTION) @property def document_types(self): """returns list of document types, e.g. "Receipts", "Handwriting" (Photos 8+ only)""" return self._get_text_for_category(self._categories.DOCUMENT_TYPE) @property def trip(self): """returns "Trips" if photo is part of a trip (Photos 8+ only)""" return self._get_first_text_for_category(self._categories.TRIP) @property def photographic_style(self): """returns photographic style, e.g. "Standard Style" (macOS 27+ only)""" return self._get_first_text_for_category(self._categories.PHOTOGRAPHIC_STYLE) @property def file_type(self): """returns file type description, e.g. "HEIF Image" (macOS 27+ only)""" return self._get_first_text_for_category(self._categories.FILE_TYPE) @property def captured_by_me(self): """returns "Captured by Me" if photo was captured by the user (macOS 27+ only)""" return self._get_first_text_for_category(self._categories.CAPTURED_BY_ME) @property def id_document_types(self): """returns list of identity document types detected in the photo, e.g. "Passport" (macOS 27+ only) Not included in all()""" return sorted( set( self._get_text_for_category(self._categories.ID_DOCUMENT_TYPE) + self._get_text_for_category(self._categories.ID_DOCUMENT_CARD_TYPE) ) ) @property def id_document_names(self): """returns list of names read from identity documents in the photo (macOS 27+ only) Not included in all()""" return self._get_text_for_category(self._categories.ID_DOCUMENT_NAME) @property def all(self): """return all search info properties in a single list""" all_ = ( self.labels + self.place_names + self.streets + self.neighborhoods + self.locality_names + self.bodies_of_water + self.holidays + self.activities + self.venues + self.venue_types + self.media_types + self.detected_text + self.times_of_day + self.areas_of_interest + self.events + self.event_performers + self.event_types + self.pets + self.age_groups + self.landmarks + self.sounds + self.human_actions + self.document_types ) if self.city: all_ += [self.city] if self.state: all_ += [self.state] if self.state_abbreviation: all_ += [self.state_abbreviation] if self.country: all_ += [self.country] if self.month: all_ += [self.month] if self.year: all_ += [self.year] if self.season: all_ += [self.season] if self.camera: all_ += [self.camera] for value in ( self.week_part, self.day_of_week, self.home, self.country_code, self.region, self.continent, self.trip, self.photographic_style, self.file_type, self.captured_by_me, ): if value: all_.append(value) return all_
[docs] def asdict(self): """return dict of search info""" return { "labels": self.labels, "place_names": self.place_names, "streets": self.streets, "neighborhoods": self.neighborhoods, "city": self.city, "locality_names": self.locality_names, "state": self.state, "state_abbreviation": self.state_abbreviation, "country": self.country, "bodies_of_water": self.bodies_of_water, "month": self.month, "year": self.year, "holidays": self.holidays, "activities": self.activities, "season": self.season, "venues": self.venues, "venue_types": self.venue_types, "media_types": self.media_types, "detected_text": self.detected_text, "camera": self.camera, "source": self.source, "times_of_day": self.times_of_day, "week_part": self.week_part, "day_of_week": self.day_of_week, "home": self.home, "areas_of_interest": self.areas_of_interest, "country_code": self.country_code, "region": self.region, "continent": self.continent, "events": self.events, "event_performers": self.event_performers, "event_types": self.event_types, "pets": self.pets, "age_groups": self.age_groups, "landmarks": self.landmarks, "sounds": self.sounds, "human_actions": self.human_actions, "document_types": self.document_types, "trip": self.trip, "photographic_style": self.photographic_style, "file_type": self.file_type, "captured_by_me": self.captured_by_me, "id_document_types": self.id_document_types, "id_document_names": self.id_document_names, }
def _get_first_text_for_category(self, category): """return first text value for a specified category ID or "" if none""" text = self._get_text_for_category(category) return text[0] if text else "" def _get_text_for_category(self, category): """return list of text for a specified category ID""" if self._db_searchinfo: content = "normalized_string" if self._normalized else "content_string" return sorted( [ rec[content] for rec in self._db_searchinfo if rec["category"] == category ] ) else: return []