Added --description-template to CLI, closes #166

This commit is contained in:
Rhet Turnbull
2020-06-28 20:10:38 -07:00
parent 59507077ba
commit bb4bc8fd96
5 changed files with 107 additions and 6 deletions

View File

@@ -1146,6 +1146,18 @@ def query(
'--keyword-template "{created.year}" ' '--keyword-template "{created.year}" '
"See Templating System below.", "See Templating System below.",
) )
@click.option(
"--description-template",
metavar="TEMPLATE",
multiple=False,
default=None,
help="For use with --exiftool, --sidecar; specify a template string to use as "
"description in the form '{name,DEFAULT}' "
"This is the same format as --directory. For example, if you wanted to append "
"'exported with osxphotos on [today's date]' to the description, you could specify "
'--description-template "{descr} exported with osxphotos on {today.date}" '
"See Templating System below.",
)
@click.option( @click.option(
"--current-name", "--current-name",
is_flag=True, is_flag=True,
@@ -1260,6 +1272,7 @@ def export(
person_keyword, person_keyword,
album_keyword, album_keyword,
keyword_template, keyword_template,
description_template,
current_name, current_name,
sidecar, sidecar,
only_photos, only_photos,
@@ -1505,6 +1518,7 @@ def export(
album_keyword=album_keyword, album_keyword=album_keyword,
person_keyword=person_keyword, person_keyword=person_keyword,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
export_db=export_db, export_db=export_db,
fileutil=fileutil, fileutil=fileutil,
dry_run=dry_run, dry_run=dry_run,
@@ -1541,6 +1555,7 @@ def export(
album_keyword=album_keyword, album_keyword=album_keyword,
person_keyword=person_keyword, person_keyword=person_keyword,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
export_db=export_db, export_db=export_db,
fileutil=fileutil, fileutil=fileutil,
dry_run=dry_run, dry_run=dry_run,
@@ -2012,6 +2027,7 @@ def export_photo(
album_keyword=None, album_keyword=None,
person_keyword=None, person_keyword=None,
keyword_template=None, keyword_template=None,
description_template=None,
export_db=None, export_db=None,
fileutil=FileUtil, fileutil=FileUtil,
dry_run=None, dry_run=None,
@@ -2039,6 +2055,7 @@ def export_photo(
album_keyword: boolean; if True, exports album names as keywords in metadata album_keyword: boolean; if True, exports album names as keywords in metadata
person_keyword: boolean; if True, exports person names as keywords in metadata person_keyword: boolean; if True, exports person names as keywords in metadata
keyword_template: list of strings; if provided use rendered template strings as keywords keyword_template: list of strings; if provided use rendered template strings as keywords
description_template: string; optional template string that will be rendered for use as photo description
export_db: export database instance compatible with ExportDB_ABC export_db: export database instance compatible with ExportDB_ABC
fileutil: file util class compatible with FileUtilABC fileutil: file util class compatible with FileUtilABC
dry_run: boolean; if True, doesn't actually export or update any files dry_run: boolean; if True, doesn't actually export or update any files
@@ -2113,6 +2130,7 @@ def export_photo(
use_albums_as_keywords=album_keyword, use_albums_as_keywords=album_keyword,
use_persons_as_keywords=person_keyword, use_persons_as_keywords=person_keyword,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
update=update, update=update,
export_db=export_db, export_db=export_db,
fileutil=fileutil, fileutil=fileutil,
@@ -2168,6 +2186,7 @@ def export_photo(
use_albums_as_keywords=album_keyword, use_albums_as_keywords=album_keyword,
use_persons_as_keywords=person_keyword, use_persons_as_keywords=person_keyword,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
update=update, update=update,
export_db=export_db, export_db=export_db,
fileutil=fileutil, fileutil=fileutil,

View File

@@ -1,3 +1,3 @@
""" version info """ """ version info """
__version__ = "0.30.2" __version__ = "0.30.3"

View File

@@ -215,6 +215,7 @@ def export(
use_albums_as_keywords=False, use_albums_as_keywords=False,
use_persons_as_keywords=False, use_persons_as_keywords=False,
keyword_template=None, keyword_template=None,
description_template=None,
): ):
""" export photo """ export photo
dest: must be valid destination path (or exception raised) dest: must be valid destination path (or exception raised)
@@ -250,6 +251,7 @@ def export(
use_persons_as_keywords: (boolean, default = False); if True, will include person names in keywords use_persons_as_keywords: (boolean, default = False); if True, will include person names in keywords
when exporting metadata with exiftool or sidecar when exporting metadata with exiftool or sidecar
keyword_template: (list of strings); list of template strings that will be rendered as used as keywords keyword_template: (list of strings); list of template strings that will be rendered as used as keywords
description_template: string; optional template string that will be rendered for use as photo description
returns: list of photos exported returns: list of photos exported
""" """
@@ -273,6 +275,7 @@ def export(
use_albums_as_keywords=use_albums_as_keywords, use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords, use_persons_as_keywords=use_persons_as_keywords,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
) )
return results.exported return results.exported
@@ -297,6 +300,7 @@ def export2(
use_albums_as_keywords=False, use_albums_as_keywords=False,
use_persons_as_keywords=False, use_persons_as_keywords=False,
keyword_template=None, keyword_template=None,
description_template=None,
update=False, update=False,
export_db=None, export_db=None,
fileutil=FileUtil, fileutil=FileUtil,
@@ -336,6 +340,7 @@ def export2(
use_persons_as_keywords: (boolean, default = False); if True, will include person names in keywords use_persons_as_keywords: (boolean, default = False); if True, will include person names in keywords
when exporting metadata with exiftool or sidecar when exporting metadata with exiftool or sidecar
keyword_template: (list of strings); list of template strings that will be rendered as used as keywords keyword_template: (list of strings); list of template strings that will be rendered as used as keywords
description_template: string; optional template string that will be rendered for use as photo description
update: (boolean, default=False); if True export will run in update mode, that is, it will update: (boolean, default=False); if True export will run in update mode, that is, it will
not export the photo if the current version already exists in the destination not export the photo if the current version already exists in the destination
export_db: (ExportDB_ABC); instance of a class that conforms to ExportDB_ABC with methods export_db: (ExportDB_ABC); instance of a class that conforms to ExportDB_ABC with methods
@@ -670,6 +675,7 @@ def export2(
use_albums_as_keywords=use_albums_as_keywords, use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords, use_persons_as_keywords=use_persons_as_keywords,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
) )
if not dry_run: if not dry_run:
try: try:
@@ -685,6 +691,7 @@ def export2(
use_albums_as_keywords=use_albums_as_keywords, use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords, use_persons_as_keywords=use_persons_as_keywords,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
) )
if not dry_run: if not dry_run:
try: try:
@@ -712,6 +719,7 @@ def export2(
use_albums_as_keywords=use_albums_as_keywords, use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords, use_persons_as_keywords=use_persons_as_keywords,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
) )
)[0] )[0]
if old_data != current_data: if old_data != current_data:
@@ -727,6 +735,7 @@ def export2(
use_albums_as_keywords=use_albums_as_keywords, use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords, use_persons_as_keywords=use_persons_as_keywords,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
) )
export_db.set_exifdata_for_file( export_db.set_exifdata_for_file(
exported_file, exported_file,
@@ -734,6 +743,7 @@ def export2(
use_albums_as_keywords=use_albums_as_keywords, use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords, use_persons_as_keywords=use_persons_as_keywords,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
), ),
) )
export_db.set_stat_exif_for_file( export_db.set_stat_exif_for_file(
@@ -749,6 +759,7 @@ def export2(
use_albums_as_keywords=use_albums_as_keywords, use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords, use_persons_as_keywords=use_persons_as_keywords,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
) )
export_db.set_exifdata_for_file( export_db.set_exifdata_for_file(
exported_file, exported_file,
@@ -756,6 +767,7 @@ def export2(
use_albums_as_keywords=use_albums_as_keywords, use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords, use_persons_as_keywords=use_persons_as_keywords,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
), ),
) )
export_db.set_stat_exif_for_file( export_db.set_stat_exif_for_file(
@@ -955,6 +967,7 @@ def _write_exif_data(
use_albums_as_keywords=False, use_albums_as_keywords=False,
use_persons_as_keywords=False, use_persons_as_keywords=False,
keyword_template=None, keyword_template=None,
description_template=None,
): ):
""" write exif data to image file at filepath """ write exif data to image file at filepath
filepath: full path to the image file """ filepath: full path to the image file """
@@ -966,6 +979,7 @@ def _write_exif_data(
use_albums_as_keywords=use_albums_as_keywords, use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords, use_persons_as_keywords=use_persons_as_keywords,
keyword_template=keyword_template, keyword_template=keyword_template,
description_template=description_template,
) )
)[0] )[0]
for exiftag, val in exif_info.items(): for exiftag, val in exif_info.items():
@@ -984,6 +998,7 @@ def _exiftool_json_sidecar(
use_albums_as_keywords=False, use_albums_as_keywords=False,
use_persons_as_keywords=False, use_persons_as_keywords=False,
keyword_template=None, keyword_template=None,
description_template=None,
): ):
""" return json string of EXIF details in exiftool sidecar format """ return json string of EXIF details in exiftool sidecar format
Does not include all the EXIF fields as those are likely already in the image Does not include all the EXIF fields as those are likely already in the image
@@ -1009,7 +1024,13 @@ def _exiftool_json_sidecar(
exif = {} exif = {}
exif["_CreatedBy"] = "osxphotos, https://github.com/RhetTbull/osxphotos" exif["_CreatedBy"] = "osxphotos, https://github.com/RhetTbull/osxphotos"
if self.description: if description_template is not None:
description = self.render_template(
description_template, expand_inplace=True, inplace_sep=", "
)[0]
exif["EXIF:ImageDescription"] = description
exif["XMP:Description"] = description
elif self.description:
exif["EXIF:ImageDescription"] = self.description exif["EXIF:ImageDescription"] = self.description
exif["XMP:Description"] = self.description exif["XMP:Description"] = self.description
@@ -1112,16 +1133,25 @@ def _xmp_sidecar(
use_albums_as_keywords=False, use_albums_as_keywords=False,
use_persons_as_keywords=False, use_persons_as_keywords=False,
keyword_template=None, keyword_template=None,
description_template=None,
): ):
""" returns string for XMP sidecar """ returns string for XMP sidecar
use_albums_as_keywords: treat album names as keywords use_albums_as_keywords: treat album names as keywords
use_persons_as_keywords: treat person names as keywords use_persons_as_keywords: treat person names as keywords
keyword_template: (list of strings); list of template strings to render as keywords """ keyword_template: (list of strings); list of template strings to render as keywords
description_template: string; optional template string that will be rendered for use as photo description """
# TODO: add additional fields to XMP file? # TODO: add additional fields to XMP file?
xmp_template = Template(filename=os.path.join(_TEMPLATE_DIR, _XMP_TEMPLATE_NAME)) xmp_template = Template(filename=os.path.join(_TEMPLATE_DIR, _XMP_TEMPLATE_NAME))
if description_template is not None:
description = self.render_template(
description_template, expand_inplace=True, inplace_sep=", "
)[0]
else:
description = self.description if self.description is not None else ""
keyword_list = [] keyword_list = []
if self.keywords: if self.keywords:
keyword_list.extend(self.keywords) keyword_list.extend(self.keywords)
@@ -1178,7 +1208,11 @@ def _xmp_sidecar(
subject_list = list(self.keywords) + person_list subject_list = list(self.keywords) + person_list
xmp_str = xmp_template.render( xmp_str = xmp_template.render(
photo=self, keywords=keyword_list, persons=person_list, subjects=subject_list photo=self,
description=description,
keywords=keyword_list,
persons=person_list,
subjects=subject_list,
) )
# remove extra lines that mako inserts from template # remove extra lines that mako inserts from template

View File

@@ -77,7 +77,7 @@
<rdf:Description rdf:about="" <rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/"> xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/">
${dc_description(photo.description)} ${dc_description(description)}
${dc_title(photo.title)} ${dc_title(photo.title)}
${dc_subject(subjects)} ${dc_subject(subjects)}
${dc_datecreated(photo.date)} ${dc_datecreated(photo.date)}

View File

@@ -197,7 +197,13 @@ CLI_EXPORT_RAW_EDITED = [
] ]
CLI_EXPORT_RAW_EDITED_ORIGINAL = ["IMG_0476_2.CR2", "IMG_0476_2_edited.jpeg"] CLI_EXPORT_RAW_EDITED_ORIGINAL = ["IMG_0476_2.CR2", "IMG_0476_2_edited.jpeg"]
CLI_UUID_DICT_15_5 = {"intrash": "71E3E212-00EB-430D-8A63-5E294B268554"} CLI_UUID_DICT_15_5 = {
"intrash": "71E3E212-00EB-430D-8A63-5E294B268554",
"template": "F12384F6-CD17-4151-ACBA-AE0E3688539E",
}
CLI_TEMPLATE_SIDECAR_FILENAME = "Pumkins1.json"
CLI_UUID_DICT_14_6 = {"intrash": "3tljdX43R8+k6peNHVrJNQ"} CLI_UUID_DICT_14_6 = {"intrash": "3tljdX43R8+k6peNHVrJNQ"}
PHOTOS_NOT_IN_TRASH_LEN_14_6 = 7 PHOTOS_NOT_IN_TRASH_LEN_14_6 = 7
@@ -1095,6 +1101,48 @@ def test_export_sidecar():
assert sorted(files) == sorted(CLI_EXPORT_SIDECAR_FILENAMES) assert sorted(files) == sorted(CLI_EXPORT_SIDECAR_FILENAMES)
def test_export_sidecar_templates():
import json
import os
import os.path
import osxphotos
from osxphotos.__main__ import cli
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
cli,
[
"export",
"--db",
os.path.join(cwd, PHOTOS_DB_15_5),
".",
"--sidecar=json",
f"--uuid={CLI_UUID_DICT_15_5['template']}",
"-V",
"--keyword-template",
"{person}",
"--description-template",
"{descr} {person} {keyword} {album}",
],
)
assert result.exit_code == 0
assert os.path.isfile(CLI_TEMPLATE_SIDECAR_FILENAME)
with open(CLI_TEMPLATE_SIDECAR_FILENAME, "r") as jsonfile:
exifdata = json.load(jsonfile)
assert (
exifdata[0]["XMP:Description"][0]
== "Girls with pumpkins Katie, Suzy Kids Pumpkin Farm, Test Album"
)
assert (
exifdata[0]["EXIF:ImageDescription"][0]
== "Girls with pumpkins Katie, Suzy Kids Pumpkin Farm, Test Album"
)
def test_export_live(): def test_export_live():
import glob import glob
import os import os