Added {filepath} template field in prep for --post-command and other goodies

This commit is contained in:
Rhet Turnbull
2021-06-13 18:40:45 -07:00
parent 2cdec3fc78
commit c0bd0ffc9f
9 changed files with 173 additions and 37 deletions

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.42.32"
__version__ = "0.42.33"

View File

@@ -1630,6 +1630,7 @@ def export(
jpeg_ext=jpeg_ext,
replace_keywords=replace_keywords,
retry=retry,
export_dir=dest,
)
if album_export and export_results.exported:
@@ -2251,6 +2252,7 @@ def export_photo(
jpeg_ext=None,
replace_keywords=False,
retry=0,
export_dir=None,
):
"""Helper function for export that does the actual export
@@ -2291,6 +2293,7 @@ def export_photo(
jpeg_ext: if not None, specify the extension to use for all JPEG images on export
replace_keywords: if True, --keyword-template replaces keywords instead of adding keywords
retry: retry up to retry # of times if there's an error
export_dir: top-level export directory for {export_dir} template
Returns:
list of path(s) of exported photo or None if photo was missing
@@ -2449,6 +2452,7 @@ def export_photo(
jpeg_ext=jpeg_ext,
replace_keywords=replace_keywords,
retry=retry,
export_dir=export_dir,
)
if export_edited and photo.hasadjustments:
@@ -2553,6 +2557,7 @@ def export_photo(
jpeg_ext=jpeg_ext,
replace_keywords=replace_keywords,
retry=retry,
export_dir=export_dir,
)
return results
@@ -2597,6 +2602,7 @@ def export_photo_with_template(
jpeg_ext,
replace_keywords,
retry,
export_dir,
):
"""Evaluate directory template then export photo to each directory"""
@@ -2647,6 +2653,8 @@ def export_photo_with_template(
results.missing.append(str(pathlib.Path(dest_path) / filename))
continue
render_options = RenderOptions(export_dir=export_dir)
tries = 0
while tries <= retry:
tries += 1
@@ -2684,6 +2692,7 @@ def export_photo_with_template(
exiftool_flags=exiftool_option,
jpeg_ext=jpeg_ext,
replace_keywords=replace_keywords,
render_options=render_options,
)
for warning_ in export_results.exiftool_warning:
verbose_(f"exiftool warning for file {warning_[0]}: {warning_[1]}")

View File

@@ -163,21 +163,27 @@ The following attributes may be used with '--xattr-template':
formatter.write("\n")
formatter.write_text(
"The following substitutions are 'path-like'. "
"The following substitutions are file or directory paths. "
+ "You can access various parts of the path using the following modifiers:"
)
formatter.write("\n")
formatter.write("{field.parent}: the parent directory\n")
formatter.write("{field.name}: the name of the file or final sub-directory\n")
formatter.write("{field.stem}: the name of the file without the extension\n")
formatter.write("{path.parent}: the parent directory\n")
formatter.write("{path.name}: the name of the file or final sub-directory\n")
formatter.write("{path.stem}: the name of the file without the extension\n")
formatter.write(
"{field.suffix}: the suffix of the file including the leading '.'\n"
"{path.suffix}: the suffix of the file including the leading '.'\n"
)
formatter.write("\n")
formatter.write_text(
"For example, if the field {export_dir} is '/Shared/Backup/Photos', "
+ "{export_dir.parent} is '/Shared/Backup'"
)
formatter.write(
"For example, if the field {export_dir} is '/Shared/Backup/Photos':\n")
formatter.write("{export_dir.parent} is '/Shared/Backup'\n")
formatter.write("\n")
formatter.write(
"If the field {filepath} is '/Shared/Backup/Photos/IMG_1234.JPG':\n")
formatter.write("{filepath.parent} is '/Shared/Backup/Photos'\n")
formatter.write("{filepath.name} is 'IMG_1234.JPG'\n")
formatter.write("{filepath.stem} is 'IMG_1234'\n")
formatter.write("{filepath.suffix} is '.JPG'\n")
formatter.write("\n")
templ_tuples = [("Substitution", "Description")]
templ_tuples.extend((k, v) for k, v in TEMPLATE_SUBSTITUTIONS_PATHLIB.items())

View File

@@ -15,6 +15,7 @@
# TODO: should this be its own PhotoExporter class?
# TODO: the various sidecar_json, sidecar_xmp, etc args should all be collapsed to a sidecar param using a bit mask
import dataclasses
import glob
import hashlib
import json
@@ -24,6 +25,7 @@ import pathlib
import re
import tempfile
from collections import namedtuple # pylint: disable=syntax-error
from typing import Optional
import photoscript
from mako.template import Template
@@ -391,7 +393,7 @@ def export(
use_persons_as_keywords=False,
keyword_template=None,
description_template=None,
export_dir=None,
render_options: Optional[RenderOptions] = None,
):
"""export photo
dest: must be valid destination path (or exception raised)
@@ -429,7 +431,7 @@ def export(
when exporting metadata with exiftool or sidecar
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
export_dir: value to use for {export_dir} template
render_options: an optional osxphotos.phototemplate.RenderOptions instance with options to pass to template renderer
Returns: list of photos exported
"""
@@ -461,7 +463,7 @@ def export(
use_persons_as_keywords=use_persons_as_keywords,
keyword_template=keyword_template,
description_template=description_template,
export_dir=export_dir,
render_options = render_options,
)
return results.exported
@@ -504,7 +506,7 @@ def export2(
persons=True,
location=True,
replace_keywords=False,
export_dir=None,
render_options: Optional[RenderOptions] = None
):
"""export photo, like export but with update and dry_run options
dest: must be valid destination path or exception raised
@@ -560,7 +562,7 @@ def export2(
persons: if True, include persons in exported metadata
location: if True, include location in exported metadata
replace_keywords: if True, keyword_template replaces any keywords, otherwise it's additive
export_dir: value to use for {export_dir} template
render_options: optional osxphotos.phototemplate.RenderOptions instance to specify options for rendering templates
Returns: ExportResults class
ExportResults has attributes:
@@ -602,6 +604,8 @@ def export2(
if verbose is None:
verbose = self._verbose
self._render_options = render_options or RenderOptions()
# suffix to add to edited files
# e.g. name will be filename_edited.jpg
edited_identifier = "_edited"
@@ -685,6 +689,7 @@ def export2(
f"destination exists ({dest}); overwrite={overwrite}, increment={increment}"
)
self._render_options.filepath = str(dest)
all_results = ExportResults()
if not use_photos_export:
# find the source file on disk and export
@@ -1598,7 +1603,7 @@ def _exiftool_dict(
)
if description_template is not None:
options = RenderOptions(expand_inplace=True, inplace_sep=", ")
options = dataclasses.replace(self._render_options, expand_inplace=True, inplace_sep=", ")
rendered = self.render_template(description_template, options)[0]
description = " ".join(rendered) if rendered else ""
exif["EXIF:ImageDescription"] = description
@@ -1637,7 +1642,7 @@ def _exiftool_dict(
if keyword_template:
rendered_keywords = []
options = RenderOptions(none_str=_OSXPHOTOS_NONE_SENTINEL, path_sep="/")
options = dataclasses.replace(self._render_options, none_str=_OSXPHOTOS_NONE_SENTINEL, path_sep="/")
for template_str in keyword_template:
rendered, unmatched = self.render_template(template_str, options)
if unmatched:
@@ -1915,7 +1920,7 @@ def _xmp_sidecar(
extension = extension.suffix[1:] if extension.suffix else None
if description_template is not None:
options = RenderOptions(expand_inplace=True, inplace_sep=", ")
options = dataclasses.replace(self._render_options, expand_inplace=True, inplace_sep=", ")
rendered = self.render_template(description_template, options)[0]
description = " ".join(rendered) if rendered else ""
else:
@@ -1948,7 +1953,7 @@ def _xmp_sidecar(
if keyword_template:
rendered_keywords = []
options = RenderOptions(none_str=_OSXPHOTOS_NONE_SENTINEL, path_sep="/")
options = dataclasses.replace(self._render_options, none_str=_OSXPHOTOS_NONE_SENTINEL, path_sep="/")
for template_str in keyword_template:
rendered, unmatched = self.render_template(template_str, options)
if unmatched:

View File

@@ -78,6 +78,9 @@ class PhotoInfo:
self._db = db
self._verbose = self._db._verbose
# TODO: remove this once refactor of PhotoExporter is done
self._render_options = RenderOptions()
@property
def filename(self):
"""filename of the picture"""

View File

@@ -146,6 +146,7 @@ TEMPLATE_SUBSTITUTIONS = {
TEMPLATE_SUBSTITUTIONS_PATHLIB = {
"{export_dir}": "The full path to the export directory",
"{filepath}": "The full path to the exported file",
}
# Permitted multi-value substitutions (each of these returns None or 1 or more values)
@@ -504,9 +505,7 @@ class PhotoTemplate:
path_sep=path_sep,
)
elif field.split(".")[0] in PATHLIB_SUBSTITUTIONS:
vals = self.get_template_value_pathlib(
field,
)
vals = self.get_template_value_pathlib(field)
else:
unmatched.append(field)
return [], unmatched
@@ -931,10 +930,7 @@ class PhotoTemplate:
return [value]
def get_template_value_pathlib(
self,
field,
):
def get_template_value_pathlib(self, field):
"""lookup value for template pathlib template fields
Args:
@@ -946,10 +942,17 @@ class PhotoTemplate:
Raises:
ValueError if no rule exists for field.
"""
if field.split(".")[0] not in PATHLIB_SUBSTITUTIONS:
field_stem = field.split(".")[0]
if field_stem not in PATHLIB_SUBSTITUTIONS:
raise ValueError(f"SyntaxError: Unknown field: {field}")
value = _get_pathlib_value(field, self.export_dir)
field_value = None
try:
field_value = getattr(self, field_stem)
except AttributeError:
raise ValueError(f"Unknown path-like field: {field_stem}")
value = _get_pathlib_value(field, field_value)
if self.filename:
value = sanitize_pathpart(value)