Added {photo} template, partial fix for issue #417
This commit is contained in:
13
README.md
13
README.md
@@ -1213,6 +1213,18 @@ Substitution Description
|
|||||||
'Restaurant'; (Photos 5+ only, applied automatically
|
'Restaurant'; (Photos 5+ only, applied automatically
|
||||||
by Photos' image categorization algorithms).
|
by Photos' image categorization algorithms).
|
||||||
|
|
||||||
|
{photo} Provides direct access to the PhotoInfo object for
|
||||||
|
the photo. Must be used in format '{photo.property}'
|
||||||
|
where 'property' represents a PhotoInfo property. For
|
||||||
|
example: '{photo.favorite}' is the same as
|
||||||
|
'{favorite}' and '{photo.place.name}' is the same as
|
||||||
|
'{place.name}'. '{photo}' provides access to
|
||||||
|
properties that are not available as separate
|
||||||
|
template fields but it assumes some knowledge of the
|
||||||
|
underlying PhotoInfo class. See
|
||||||
|
https://rhettbull.github.io/osxphotos/ for additional
|
||||||
|
documentation on the PhotoInfo class.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -2788,6 +2800,7 @@ The following template field substitutions are availabe for use with `PhotoInfo.
|
|||||||
|{searchinfo.activity}|Activities associated with a photo, e.g. 'Sporting Event'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).|
|
|{searchinfo.activity}|Activities associated with a photo, e.g. 'Sporting Event'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).|
|
||||||
|{searchinfo.venue}|Venues associated with a photo, e.g. name of restaurant; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).|
|
|{searchinfo.venue}|Venues associated with a photo, e.g. name of restaurant; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).|
|
||||||
|{searchinfo.venue_type}|Venue types associated with a photo, e.g. 'Restaurant'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).|
|
|{searchinfo.venue_type}|Venue types associated with a photo, e.g. 'Restaurant'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).|
|
||||||
|
|{photo}|Provides direct access to the PhotoInfo object for the photo. Must be used in format '{photo.property}' where 'property' represents a PhotoInfo property. For example: '{photo.favorite}' is the same as '{favorite}' and '{photo.place.name}' is the same as '{place.name}'. '{photo}' provides access to properties that are not available as separate template fields but it assumes some knowledge of the underlying PhotoInfo class. See https://rhettbull.github.io/osxphotos/ for additional documentation on the PhotoInfo class.|
|
||||||
<!-- OSXPHOTOS-TEMPLATE-TABLE:END -->
|
<!-- OSXPHOTOS-TEMPLATE-TABLE:END -->
|
||||||
|
|
||||||
### Utility Functions
|
### Utility Functions
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.41.10"
|
__version__ = "0.41.11"
|
||||||
|
|||||||
@@ -146,6 +146,11 @@ TEMPLATE_SUBSTITUTIONS_MULTI_VALUED = {
|
|||||||
"{searchinfo.activity}": "Activities associated with a photo, e.g. 'Sporting Event'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).",
|
"{searchinfo.activity}": "Activities associated with a photo, e.g. 'Sporting Event'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).",
|
||||||
"{searchinfo.venue}": "Venues associated with a photo, e.g. name of restaurant; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).",
|
"{searchinfo.venue}": "Venues associated with a photo, e.g. name of restaurant; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).",
|
||||||
"{searchinfo.venue_type}": "Venue types associated with a photo, e.g. 'Restaurant'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).",
|
"{searchinfo.venue_type}": "Venue types associated with a photo, e.g. 'Restaurant'; (Photos 5+ only, applied automatically by Photos' image categorization algorithms).",
|
||||||
|
"{photo}": "Provides direct access to the PhotoInfo object for the photo. "
|
||||||
|
+ "Must be used in format '{photo.property}' where 'property' represents a PhotoInfo property. "
|
||||||
|
+ "For example: '{photo.favorite}' is the same as '{favorite}' and '{photo.place.name}' is the same as '{place.name}'. "
|
||||||
|
+ "'{photo}' provides access to properties that are not available as separate template fields but it assumes some knowledge of "
|
||||||
|
+ "the underlying PhotoInfo class. See https://rhettbull.github.io/osxphotos/ for additional documentation on the PhotoInfo class.",
|
||||||
}
|
}
|
||||||
|
|
||||||
FILTER_VALUES = {
|
FILTER_VALUES = {
|
||||||
@@ -363,7 +368,7 @@ class PhotoTemplate:
|
|||||||
if ts.template:
|
if ts.template:
|
||||||
# have a template field to process
|
# have a template field to process
|
||||||
field = ts.template.field
|
field = ts.template.field
|
||||||
if field not in FIELD_NAMES:
|
if field not in FIELD_NAMES and not field.startswith("photo"):
|
||||||
unmatched.append(field)
|
unmatched.append(field)
|
||||||
return [], unmatched
|
return [], unmatched
|
||||||
|
|
||||||
@@ -443,7 +448,7 @@ class PhotoTemplate:
|
|||||||
vals = self.get_template_value_exiftool(
|
vals = self.get_template_value_exiftool(
|
||||||
subfield, filename=filename, dirname=dirname
|
subfield, filename=filename, dirname=dirname
|
||||||
)
|
)
|
||||||
elif field in MULTI_VALUE_SUBSTITUTIONS:
|
elif field in MULTI_VALUE_SUBSTITUTIONS or field.startswith("photo"):
|
||||||
vals = self.get_template_value_multi(
|
vals = self.get_template_value_multi(
|
||||||
field, path_sep=path_sep, filename=filename, dirname=dirname
|
field, path_sep=path_sep, filename=filename, dirname=dirname
|
||||||
)
|
)
|
||||||
@@ -894,6 +899,32 @@ class PhotoTemplate:
|
|||||||
values = (
|
values = (
|
||||||
self.photo.search_info.venue_types if self.photo.search_info else []
|
self.photo.search_info.venue_types if self.photo.search_info else []
|
||||||
)
|
)
|
||||||
|
elif field.startswith("photo"):
|
||||||
|
# provide access to PhotoInfo object
|
||||||
|
properties = field.split(".")
|
||||||
|
if len(properties) <= 1:
|
||||||
|
raise ValueError(
|
||||||
|
"Missing property in {photo} template. Use in form {photo.property}."
|
||||||
|
)
|
||||||
|
obj = self.photo
|
||||||
|
for i in range(1, len(properties)):
|
||||||
|
property_ = properties[i]
|
||||||
|
try:
|
||||||
|
obj = getattr(obj, property_)
|
||||||
|
if obj is None:
|
||||||
|
break
|
||||||
|
except AttributeError:
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid property for {photo} template: " + f"'{property_}'"
|
||||||
|
)
|
||||||
|
if obj is None:
|
||||||
|
values = []
|
||||||
|
elif isinstance(obj, bool):
|
||||||
|
values = [property_] if obj else []
|
||||||
|
elif isinstance(obj, (str, int, float)):
|
||||||
|
values = [str(obj)]
|
||||||
|
else:
|
||||||
|
values = [val for val in obj]
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unhandled template value: {field}")
|
raise ValueError(f"Unhandled template value: {field}")
|
||||||
|
|
||||||
|
|||||||
@@ -256,6 +256,25 @@ COMMENT_UUID_DICT = {
|
|||||||
"4E4944A0-3E5C-4028-9600-A8709F2FA1DB": ["None: Nice trophy"],
|
"4E4944A0-3E5C-4028-9600-A8709F2FA1DB": ["None: Nice trophy"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UUID_PHOTO = {
|
||||||
|
"DC99FBDD-7A52-4100-A5BB-344131646C30": {
|
||||||
|
"{photo.title}": ["St. James's Park"],
|
||||||
|
"{photo.favorite?FAVORITE,NOTFAVORITE}": ["NOTFAVORITE"],
|
||||||
|
"{photo.hdr}": ["_"],
|
||||||
|
"{photo.keywords}": [
|
||||||
|
"England",
|
||||||
|
"London",
|
||||||
|
"London 2018",
|
||||||
|
"St. James's Park",
|
||||||
|
"UK",
|
||||||
|
"United Kingdom",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"3DD2C897-F19E-4CA6-8C22-B027D5A71907": {"{photo.place.country_code}": ["AU"]},
|
||||||
|
"F12384F6-CD17-4151-ACBA-AE0E3688539E": {"{photo.place.name}": ["_"]},
|
||||||
|
"E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51": {"{photo.favorite}": ["favorite"]},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def photosdb_places():
|
def photosdb_places():
|
||||||
@@ -310,7 +329,7 @@ def test_lookup_multi(photosdb_places):
|
|||||||
|
|
||||||
for subst in TEMPLATE_SUBSTITUTIONS_MULTI_VALUED:
|
for subst in TEMPLATE_SUBSTITUTIONS_MULTI_VALUED:
|
||||||
lookup_str = re.match(r"\{([^\\,}]+)\}", subst).group(1)
|
lookup_str = re.match(r"\{([^\\,}]+)\}", subst).group(1)
|
||||||
if subst == "{exiftool}":
|
if subst in ["{exiftool}", "{photo}"]:
|
||||||
continue
|
continue
|
||||||
lookup = template.get_template_value_multi(lookup_str, path_sep=os.path.sep)
|
lookup = template.get_template_value_multi(lookup_str, path_sep=os.path.sep)
|
||||||
assert isinstance(lookup, list)
|
assert isinstance(lookup, list)
|
||||||
@@ -879,3 +898,10 @@ def test_punctuation(photosdb):
|
|||||||
rendered, _ = photo.render_template("{" + punc + "}")
|
rendered, _ = photo.render_template("{" + punc + "}")
|
||||||
assert rendered[0] == PUNCTUATION[punc]
|
assert rendered[0] == PUNCTUATION[punc]
|
||||||
|
|
||||||
|
|
||||||
|
def test_photo_template(photosdb):
|
||||||
|
for uuid in UUID_PHOTO:
|
||||||
|
photo = photosdb.get_photo(uuid)
|
||||||
|
for template in UUID_PHOTO[uuid]:
|
||||||
|
rendered, _ = photo.render_template(template)
|
||||||
|
assert sorted(rendered) == sorted(UUID_PHOTO[uuid][template])
|
||||||
|
|||||||
Reference in New Issue
Block a user