@@ -373,6 +373,12 @@ Options:
|
|||||||
may be specified by repeating the option,
|
may be specified by repeating the option,
|
||||||
e.g. --exiftool-option '-m' --exiftool-
|
e.g. --exiftool-option '-m' --exiftool-
|
||||||
option '-F'.
|
option '-F'.
|
||||||
|
--exiftool-merge-keywords Merge any keywords found in the original
|
||||||
|
file with keywords used for '--exiftool' and
|
||||||
|
'--sidecar'.
|
||||||
|
--exiftool-merge-persons Merge any persons found in the original file
|
||||||
|
with persons used for '--exiftool' and '--
|
||||||
|
sidecar'.
|
||||||
--ignore-date-modified If used with --exiftool or --sidecar, will
|
--ignore-date-modified If used with --exiftool or --sidecar, will
|
||||||
ignore the photo modification date and set
|
ignore the photo modification date and set
|
||||||
EXIF:ModifyDate to EXIF:DateTimeOriginal;
|
EXIF:ModifyDate to EXIF:DateTimeOriginal;
|
||||||
|
|||||||
@@ -1394,6 +1394,16 @@ def query(
|
|||||||
"More than one option may be specified by repeating the option, e.g. "
|
"More than one option may be specified by repeating the option, e.g. "
|
||||||
"--exiftool-option '-m' --exiftool-option '-F'. ",
|
"--exiftool-option '-m' --exiftool-option '-F'. ",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--exiftool-merge-keywords",
|
||||||
|
is_flag=True,
|
||||||
|
help="Merge any keywords found in the original file with keywords used for '--exiftool' and '--sidecar'.",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--exiftool-merge-persons",
|
||||||
|
is_flag=True,
|
||||||
|
help="Merge any persons found in the original file with persons used for '--exiftool' and '--sidecar'.",
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--ignore-date-modified",
|
"--ignore-date-modified",
|
||||||
is_flag=True,
|
is_flag=True,
|
||||||
@@ -1592,6 +1602,8 @@ def export(
|
|||||||
dest,
|
dest,
|
||||||
exiftool,
|
exiftool,
|
||||||
exiftool_option,
|
exiftool_option,
|
||||||
|
exiftool_merge_keywords,
|
||||||
|
exiftool_merge_persons,
|
||||||
ignore_date_modified,
|
ignore_date_modified,
|
||||||
portrait,
|
portrait,
|
||||||
not_portrait,
|
not_portrait,
|
||||||
@@ -1714,6 +1726,7 @@ def export(
|
|||||||
convert_to_jpeg = cfg.convert_to_jpeg
|
convert_to_jpeg = cfg.convert_to_jpeg
|
||||||
jpeg_quality = cfg.jpeg_quality
|
jpeg_quality = cfg.jpeg_quality
|
||||||
sidecar = cfg.sidecar
|
sidecar = cfg.sidecar
|
||||||
|
sidecar_drop_ext = cfg.sidecar_drop_ext
|
||||||
only_photos = cfg.only_photos
|
only_photos = cfg.only_photos
|
||||||
only_movies = cfg.only_movies
|
only_movies = cfg.only_movies
|
||||||
burst = cfg.burst
|
burst = cfg.burst
|
||||||
@@ -1722,6 +1735,9 @@ def export(
|
|||||||
not_live = cfg.not_live
|
not_live = cfg.not_live
|
||||||
download_missing = cfg.download_missing
|
download_missing = cfg.download_missing
|
||||||
exiftool = cfg.exiftool
|
exiftool = cfg.exiftool
|
||||||
|
exiftool_option = cfg.exiftool_option
|
||||||
|
exiftool_merge_keywords = cfg.exiftool_merge_keywords
|
||||||
|
exiftool_merge_persons = cfg.exiftool_merge_persons
|
||||||
ignore_date_modified = cfg.ignore_date_modified
|
ignore_date_modified = cfg.ignore_date_modified
|
||||||
portrait = cfg.portrait
|
portrait = cfg.portrait
|
||||||
not_portrait = cfg.not_portrait
|
not_portrait = cfg.not_portrait
|
||||||
@@ -1795,6 +1811,8 @@ def export(
|
|||||||
("jpeg_quality", ("convert_to_jpeg")),
|
("jpeg_quality", ("convert_to_jpeg")),
|
||||||
("ignore_signature", ("update")),
|
("ignore_signature", ("update")),
|
||||||
("exiftool_option", ("exiftool")),
|
("exiftool_option", ("exiftool")),
|
||||||
|
("exiftool_merge_keywords", ("exiftool", "sidecar")),
|
||||||
|
("exiftool_merge_persons", ("exiftool", "sidecar")),
|
||||||
]
|
]
|
||||||
try:
|
try:
|
||||||
cfg.validate(exclusive=exclusive_options, dependent=dependent_options, cli=True)
|
cfg.validate(exclusive=exclusive_options, dependent=dependent_options, cli=True)
|
||||||
@@ -2058,6 +2076,8 @@ def export(
|
|||||||
export_live=export_live,
|
export_live=export_live,
|
||||||
download_missing=download_missing,
|
download_missing=download_missing,
|
||||||
exiftool=exiftool,
|
exiftool=exiftool,
|
||||||
|
exiftool_merge_keywords=exiftool_merge_keywords,
|
||||||
|
exiftool_merge_persons=exiftool_merge_persons,
|
||||||
directory=directory,
|
directory=directory,
|
||||||
filename_template=filename_template,
|
filename_template=filename_template,
|
||||||
export_raw=export_raw,
|
export_raw=export_raw,
|
||||||
@@ -2107,6 +2127,8 @@ def export(
|
|||||||
export_live=export_live,
|
export_live=export_live,
|
||||||
download_missing=download_missing,
|
download_missing=download_missing,
|
||||||
exiftool=exiftool,
|
exiftool=exiftool,
|
||||||
|
exiftool_merge_keywords=exiftool_merge_keywords,
|
||||||
|
exiftool_merge_persons=exiftool_merge_persons,
|
||||||
directory=directory,
|
directory=directory,
|
||||||
filename_template=filename_template,
|
filename_template=filename_template,
|
||||||
export_raw=export_raw,
|
export_raw=export_raw,
|
||||||
@@ -2640,6 +2662,8 @@ def export_photo(
|
|||||||
export_live=None,
|
export_live=None,
|
||||||
download_missing=None,
|
download_missing=None,
|
||||||
exiftool=None,
|
exiftool=None,
|
||||||
|
exiftool_merge_keywords=False,
|
||||||
|
exiftool_merge_persons=False,
|
||||||
directory=None,
|
directory=None,
|
||||||
filename_template=None,
|
filename_template=None,
|
||||||
export_raw=None,
|
export_raw=None,
|
||||||
@@ -2694,6 +2718,8 @@ def export_photo(
|
|||||||
jpeg_quality: float in range 0.0 <= jpeg_quality <= 1.0. A value of 1.0 specifies use best quality, a value of 0.0 specifies use maximum compression.
|
jpeg_quality: float in range 0.0 <= jpeg_quality <= 1.0. A value of 1.0 specifies use best quality, a value of 0.0 specifies use maximum compression.
|
||||||
ignore_date_modified: if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
|
ignore_date_modified: if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
|
||||||
exiftool_option: optional list flags (e.g. ["-m", "-F"]) to pass to exiftool
|
exiftool_option: optional list flags (e.g. ["-m", "-F"]) to pass to exiftool
|
||||||
|
exiftool_merge_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)
|
||||||
|
exiftool_merge_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list of path(s) of exported photo or None if photo was missing
|
list of path(s) of exported photo or None if photo was missing
|
||||||
@@ -2826,6 +2852,8 @@ def export_photo(
|
|||||||
overwrite=overwrite,
|
overwrite=overwrite,
|
||||||
use_photos_export=use_photos_export,
|
use_photos_export=use_photos_export,
|
||||||
exiftool=exiftool,
|
exiftool=exiftool,
|
||||||
|
merge_exif_keywords=exiftool_merge_keywords,
|
||||||
|
merge_exif_persons=exiftool_merge_persons,
|
||||||
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,
|
||||||
@@ -2930,6 +2958,8 @@ def export_photo(
|
|||||||
edited=True,
|
edited=True,
|
||||||
use_photos_export=use_photos_export,
|
use_photos_export=use_photos_export,
|
||||||
exiftool=exiftool,
|
exiftool=exiftool,
|
||||||
|
merge_exif_keywords=exiftool_merge_keywords,
|
||||||
|
merge_exif_persons=exiftool_merge_persons,
|
||||||
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,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
""" version info """
|
""" version info """
|
||||||
|
|
||||||
__version__ = "0.38.17"
|
__version__ = "0.38.18"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
_export_photo
|
_export_photo
|
||||||
_write_exif_data
|
_write_exif_data
|
||||||
_exiftool_json_sidecar
|
_exiftool_json_sidecar
|
||||||
|
_get_exif_keywords
|
||||||
|
_get_exif_persons
|
||||||
_exiftool_dict
|
_exiftool_dict
|
||||||
_xmp_sidecar
|
_xmp_sidecar
|
||||||
_write_sidecar
|
_write_sidecar
|
||||||
@@ -450,6 +452,8 @@ def export2(
|
|||||||
use_photokit=False,
|
use_photokit=False,
|
||||||
verbose=None,
|
verbose=None,
|
||||||
exiftool_flags=None,
|
exiftool_flags=None,
|
||||||
|
merge_exif_keywords=False,
|
||||||
|
merge_exif_persons=False,
|
||||||
):
|
):
|
||||||
"""export photo, like export but with update and dry_run options
|
"""export photo, like export but with update and dry_run options
|
||||||
dest: must be valid destination path or exception raised
|
dest: must be valid destination path or exception raised
|
||||||
@@ -499,6 +503,8 @@ def export2(
|
|||||||
ignore_date_modified: for use with sidecar and exiftool; if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
|
ignore_date_modified: for use with sidecar and exiftool; if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
|
||||||
verbose: optional callable function to use for printing verbose text during processing; if None (default), does not print output.
|
verbose: optional callable function to use for printing verbose text during processing; if None (default), does not print output.
|
||||||
exiftool_flags: optional list of flags to pass to exiftool when using exiftool option, e.g ["-m", "-F"]
|
exiftool_flags: optional list of flags to pass to exiftool when using exiftool option, e.g ["-m", "-F"]
|
||||||
|
merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)
|
||||||
|
merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)
|
||||||
|
|
||||||
Returns: ExportResults class
|
Returns: ExportResults class
|
||||||
ExportResults has attributes:
|
ExportResults has attributes:
|
||||||
@@ -904,6 +910,8 @@ def export2(
|
|||||||
keyword_template=keyword_template,
|
keyword_template=keyword_template,
|
||||||
description_template=description_template,
|
description_template=description_template,
|
||||||
ignore_date_modified=ignore_date_modified,
|
ignore_date_modified=ignore_date_modified,
|
||||||
|
merge_exif_keywords=merge_exif_keywords,
|
||||||
|
merge_exif_persons=merge_exif_persons,
|
||||||
)
|
)
|
||||||
sidecars.append(
|
sidecars.append(
|
||||||
(
|
(
|
||||||
@@ -924,6 +932,8 @@ def export2(
|
|||||||
description_template=description_template,
|
description_template=description_template,
|
||||||
ignore_date_modified=ignore_date_modified,
|
ignore_date_modified=ignore_date_modified,
|
||||||
tag_groups=False,
|
tag_groups=False,
|
||||||
|
merge_exif_keywords=merge_exif_keywords,
|
||||||
|
merge_exif_persons=merge_exif_persons,
|
||||||
)
|
)
|
||||||
sidecars.append(
|
sidecars.append(
|
||||||
(
|
(
|
||||||
@@ -1012,6 +1022,8 @@ def export2(
|
|||||||
keyword_template=keyword_template,
|
keyword_template=keyword_template,
|
||||||
description_template=description_template,
|
description_template=description_template,
|
||||||
ignore_date_modified=ignore_date_modified,
|
ignore_date_modified=ignore_date_modified,
|
||||||
|
merge_exif_keywords=merge_exif_keywords,
|
||||||
|
merge_exif_persons=merge_exif_persons,
|
||||||
)
|
)
|
||||||
)[0]
|
)[0]
|
||||||
if old_data != current_data:
|
if old_data != current_data:
|
||||||
@@ -1030,6 +1042,8 @@ def export2(
|
|||||||
description_template=description_template,
|
description_template=description_template,
|
||||||
ignore_date_modified=ignore_date_modified,
|
ignore_date_modified=ignore_date_modified,
|
||||||
flags=exiftool_flags,
|
flags=exiftool_flags,
|
||||||
|
merge_exif_keywords=merge_exif_keywords,
|
||||||
|
merge_exif_persons=merge_exif_persons,
|
||||||
)
|
)
|
||||||
if warning_:
|
if warning_:
|
||||||
exiftool_warning.append((exported_file, warning_))
|
exiftool_warning.append((exported_file, warning_))
|
||||||
@@ -1045,6 +1059,8 @@ def export2(
|
|||||||
keyword_template=keyword_template,
|
keyword_template=keyword_template,
|
||||||
description_template=description_template,
|
description_template=description_template,
|
||||||
ignore_date_modified=ignore_date_modified,
|
ignore_date_modified=ignore_date_modified,
|
||||||
|
merge_exif_keywords=merge_exif_keywords,
|
||||||
|
merge_exif_persons=merge_exif_persons,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
export_db.set_stat_exif_for_file(
|
export_db.set_stat_exif_for_file(
|
||||||
@@ -1065,6 +1081,8 @@ def export2(
|
|||||||
description_template=description_template,
|
description_template=description_template,
|
||||||
ignore_date_modified=ignore_date_modified,
|
ignore_date_modified=ignore_date_modified,
|
||||||
flags=exiftool_flags,
|
flags=exiftool_flags,
|
||||||
|
merge_exif_keywords=merge_exif_keywords,
|
||||||
|
merge_exif_persons=merge_exif_persons,
|
||||||
)
|
)
|
||||||
if warning_:
|
if warning_:
|
||||||
exiftool_warning.append((exported_file, warning_))
|
exiftool_warning.append((exported_file, warning_))
|
||||||
@@ -1080,6 +1098,8 @@ def export2(
|
|||||||
keyword_template=keyword_template,
|
keyword_template=keyword_template,
|
||||||
description_template=description_template,
|
description_template=description_template,
|
||||||
ignore_date_modified=ignore_date_modified,
|
ignore_date_modified=ignore_date_modified,
|
||||||
|
merge_exif_keywords=merge_exif_keywords,
|
||||||
|
merge_exif_persons=merge_exif_persons,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
export_db.set_stat_exif_for_file(
|
export_db.set_stat_exif_for_file(
|
||||||
@@ -1308,6 +1328,8 @@ def _write_exif_data(
|
|||||||
description_template=None,
|
description_template=None,
|
||||||
ignore_date_modified=False,
|
ignore_date_modified=False,
|
||||||
flags=None,
|
flags=None,
|
||||||
|
merge_exif_keywords=False,
|
||||||
|
merge_exif_persons=False,
|
||||||
):
|
):
|
||||||
"""write exif data to image file at filepath
|
"""write exif data to image file at filepath
|
||||||
|
|
||||||
@@ -1330,6 +1352,8 @@ def _write_exif_data(
|
|||||||
keyword_template=keyword_template,
|
keyword_template=keyword_template,
|
||||||
description_template=description_template,
|
description_template=description_template,
|
||||||
ignore_date_modified=ignore_date_modified,
|
ignore_date_modified=ignore_date_modified,
|
||||||
|
merge_exif_keywords=merge_exif_keywords,
|
||||||
|
merge_exif_persons=merge_exif_persons,
|
||||||
)
|
)
|
||||||
|
|
||||||
with ExifTool(filepath, flags=flags) as exiftool:
|
with ExifTool(filepath, flags=flags) as exiftool:
|
||||||
@@ -1349,6 +1373,8 @@ def _exiftool_dict(
|
|||||||
keyword_template=None,
|
keyword_template=None,
|
||||||
description_template=None,
|
description_template=None,
|
||||||
ignore_date_modified=False,
|
ignore_date_modified=False,
|
||||||
|
merge_exif_keywords=False,
|
||||||
|
merge_exif_persons=False,
|
||||||
):
|
):
|
||||||
"""Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.
|
"""Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.
|
||||||
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.
|
||||||
@@ -1359,6 +1385,8 @@ def _exiftool_dict(
|
|||||||
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: (list of strings); list of template strings to render for the description
|
description_template: (list of strings); list of template strings to render for the description
|
||||||
ignore_date_modified: if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
|
ignore_date_modified: if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
|
||||||
|
merge_exif_keywords: merge keywords in the file's exif metadata (requires exiftool)
|
||||||
|
merge_exif_persons: merge persons in the file's exif metadata (requires exiftool)
|
||||||
|
|
||||||
Returns: dict with exiftool tags / values
|
Returns: dict with exiftool tags / values
|
||||||
|
|
||||||
@@ -1401,13 +1429,19 @@ def _exiftool_dict(
|
|||||||
exif["XMP:Title"] = self.title
|
exif["XMP:Title"] = self.title
|
||||||
|
|
||||||
keyword_list = []
|
keyword_list = []
|
||||||
|
if merge_exif_keywords:
|
||||||
|
keyword_list.extend(self._get_exif_keywords())
|
||||||
|
|
||||||
if self.keywords:
|
if self.keywords:
|
||||||
keyword_list.extend(self.keywords)
|
keyword_list.extend(self.keywords)
|
||||||
|
|
||||||
person_list = []
|
person_list = []
|
||||||
|
if merge_exif_persons:
|
||||||
|
person_list.extend(self._get_exif_persons())
|
||||||
|
|
||||||
if self.persons:
|
if self.persons:
|
||||||
# filter out _UNKNOWN_PERSON
|
# filter out _UNKNOWN_PERSON
|
||||||
person_list = [p for p in self.persons if p != _UNKNOWN_PERSON]
|
person_list.extend([p for p in self.persons if p != _UNKNOWN_PERSON])
|
||||||
|
|
||||||
if use_persons_as_keywords and person_list:
|
if use_persons_as_keywords and person_list:
|
||||||
keyword_list.extend(person_list)
|
keyword_list.extend(person_list)
|
||||||
@@ -1534,6 +1568,39 @@ def _exiftool_dict(
|
|||||||
return exif
|
return exif
|
||||||
|
|
||||||
|
|
||||||
|
def _get_exif_keywords(self):
|
||||||
|
""" returns list of keywords found in the file's exif metadata """
|
||||||
|
keywords = []
|
||||||
|
exif = self.exiftool
|
||||||
|
if exif:
|
||||||
|
exifdict = exif.asdict()
|
||||||
|
for field in ["IPTC:Keywords", "XMP:TagsList", "XMP:Subject"]:
|
||||||
|
try:
|
||||||
|
kw = exifdict[field]
|
||||||
|
if kw and type(kw) != list:
|
||||||
|
kw = [kw]
|
||||||
|
keywords.extend(kw)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
return keywords
|
||||||
|
|
||||||
|
|
||||||
|
def _get_exif_persons(self):
|
||||||
|
""" returns list of persons found in the file's exif metadata """
|
||||||
|
persons = []
|
||||||
|
exif = self.exiftool
|
||||||
|
if exif:
|
||||||
|
exifdict = exif.asdict()
|
||||||
|
try:
|
||||||
|
p = exifdict["XMP:PersonInImage"]
|
||||||
|
if p and type(p) != list:
|
||||||
|
p = [p]
|
||||||
|
persons.extend(p)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
return persons
|
||||||
|
|
||||||
|
|
||||||
def _exiftool_json_sidecar(
|
def _exiftool_json_sidecar(
|
||||||
self,
|
self,
|
||||||
use_albums_as_keywords=False,
|
use_albums_as_keywords=False,
|
||||||
@@ -1542,6 +1609,8 @@ def _exiftool_json_sidecar(
|
|||||||
description_template=None,
|
description_template=None,
|
||||||
ignore_date_modified=False,
|
ignore_date_modified=False,
|
||||||
tag_groups=True,
|
tag_groups=True,
|
||||||
|
merge_exif_keywords=False,
|
||||||
|
merge_exif_persons=False,
|
||||||
):
|
):
|
||||||
"""Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.
|
"""Return dict of EXIF details for building exiftool JSON sidecar or sending commands to ExifTool.
|
||||||
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.
|
||||||
@@ -1553,6 +1622,8 @@ def _exiftool_json_sidecar(
|
|||||||
description_template: (list of strings); list of template strings to render for the description
|
description_template: (list of strings); list of template strings to render for the description
|
||||||
ignore_date_modified: if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
|
ignore_date_modified: if True, sets EXIF:ModifyDate to EXIF:DateTimeOriginal even if date_modified is set
|
||||||
tag_groups: if True, tags are in form Group:TagName, e.g. IPTC:Keywords, otherwise group name is omitted, e.g. Keywords
|
tag_groups: if True, tags are in form Group:TagName, e.g. IPTC:Keywords, otherwise group name is omitted, e.g. Keywords
|
||||||
|
merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)
|
||||||
|
merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)
|
||||||
|
|
||||||
Returns: dict with exiftool tags / values
|
Returns: dict with exiftool tags / values
|
||||||
|
|
||||||
@@ -1584,6 +1655,8 @@ def _exiftool_json_sidecar(
|
|||||||
keyword_template=keyword_template,
|
keyword_template=keyword_template,
|
||||||
description_template=description_template,
|
description_template=description_template,
|
||||||
ignore_date_modified=ignore_date_modified,
|
ignore_date_modified=ignore_date_modified,
|
||||||
|
merge_exif_keywords=merge_exif_keywords,
|
||||||
|
merge_exif_persons=merge_exif_persons,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not tag_groups:
|
if not tag_groups:
|
||||||
@@ -1604,12 +1677,17 @@ def _xmp_sidecar(
|
|||||||
keyword_template=None,
|
keyword_template=None,
|
||||||
description_template=None,
|
description_template=None,
|
||||||
extension=None,
|
extension=None,
|
||||||
|
merge_exif_keywords=False,
|
||||||
|
merge_exif_persons=False,
|
||||||
):
|
):
|
||||||
"""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"""
|
description_template: string; optional template string that will be rendered for use as photo description
|
||||||
|
merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)
|
||||||
|
merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)
|
||||||
|
"""
|
||||||
|
|
||||||
xmp_template = Template(filename=os.path.join(_TEMPLATE_DIR, _XMP_TEMPLATE_NAME))
|
xmp_template = Template(filename=os.path.join(_TEMPLATE_DIR, _XMP_TEMPLATE_NAME))
|
||||||
|
|
||||||
@@ -1626,6 +1704,9 @@ def _xmp_sidecar(
|
|||||||
description = self.description if self.description is not None else ""
|
description = self.description if self.description is not None else ""
|
||||||
|
|
||||||
keyword_list = []
|
keyword_list = []
|
||||||
|
if merge_exif_keywords:
|
||||||
|
keyword_list.extend(self._get_exif_keywords())
|
||||||
|
|
||||||
if self.keywords:
|
if self.keywords:
|
||||||
keyword_list.extend(self.keywords)
|
keyword_list.extend(self.keywords)
|
||||||
|
|
||||||
@@ -1633,9 +1714,12 @@ def _xmp_sidecar(
|
|||||||
# good candidate for pulling out in a function
|
# good candidate for pulling out in a function
|
||||||
|
|
||||||
person_list = []
|
person_list = []
|
||||||
|
if merge_exif_persons:
|
||||||
|
person_list.extend(self._get_exif_persons())
|
||||||
|
|
||||||
if self.persons:
|
if self.persons:
|
||||||
# filter out _UNKNOWN_PERSON
|
# filter out _UNKNOWN_PERSON
|
||||||
person_list = [p for p in self.persons if p != _UNKNOWN_PERSON]
|
person_list.extend([p for p in self.persons if p != _UNKNOWN_PERSON])
|
||||||
|
|
||||||
if use_persons_as_keywords and person_list:
|
if use_persons_as_keywords and person_list:
|
||||||
keyword_list.extend(person_list)
|
keyword_list.extend(person_list)
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ from .._constants import (
|
|||||||
_PHOTOS_4_ALBUM_KIND,
|
_PHOTOS_4_ALBUM_KIND,
|
||||||
_PHOTOS_4_ROOT_FOLDER,
|
_PHOTOS_4_ROOT_FOLDER,
|
||||||
_PHOTOS_4_VERSION,
|
_PHOTOS_4_VERSION,
|
||||||
_PHOTOS_5_VERSION,
|
|
||||||
_PHOTOS_5_ALBUM_KIND,
|
_PHOTOS_5_ALBUM_KIND,
|
||||||
_PHOTOS_5_IMPORT_SESSION_ALBUM_KIND,
|
_PHOTOS_5_IMPORT_SESSION_ALBUM_KIND,
|
||||||
_PHOTOS_5_SHARED_ALBUM_KIND,
|
_PHOTOS_5_SHARED_ALBUM_KIND,
|
||||||
_PHOTOS_5_SHARED_PHOTO_PATH,
|
_PHOTOS_5_SHARED_PHOTO_PATH,
|
||||||
|
_PHOTOS_5_VERSION,
|
||||||
)
|
)
|
||||||
from ..albuminfo import AlbumInfo, ImportInfo
|
from ..albuminfo import AlbumInfo, ImportInfo
|
||||||
from ..personinfo import FaceInfo, PersonInfo
|
from ..personinfo import FaceInfo, PersonInfo
|
||||||
@@ -56,6 +56,8 @@ class PhotoInfo:
|
|||||||
_export_photo,
|
_export_photo,
|
||||||
_exiftool_dict,
|
_exiftool_dict,
|
||||||
_exiftool_json_sidecar,
|
_exiftool_json_sidecar,
|
||||||
|
_get_exif_keywords,
|
||||||
|
_get_exif_persons,
|
||||||
_write_exif_data,
|
_write_exif_data,
|
||||||
_write_sidecar,
|
_write_sidecar,
|
||||||
_xmp_sidecar,
|
_xmp_sidecar,
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 574 KiB After Width: | Height: | Size: 577 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 528 KiB After Width: | Height: | Size: 532 KiB |
@@ -367,6 +367,29 @@ CLI_EXIFTOOL = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CLI_EXIFTOOL_MERGE = {
|
||||||
|
"1EB2B765-0765-43BA-A90C-0D0580E6172C": {
|
||||||
|
"File:FileName": "Pumpkins3.jpg",
|
||||||
|
"IPTC:Keywords": "Kids",
|
||||||
|
"XMP:TagsList": "Kids",
|
||||||
|
"EXIF:ImageDescription": "Kids in pumpkin field",
|
||||||
|
"XMP:Description": "Kids in pumpkin field",
|
||||||
|
"XMP:PersonInImage": ["Katie", "Suzy", "Tim"],
|
||||||
|
"XMP:Subject": "Kids",
|
||||||
|
},
|
||||||
|
"D79B8D77-BFFC-460B-9312-034F2877D35B": {
|
||||||
|
"File:FileName": "Pumkins2.jpg",
|
||||||
|
"XMP:Title": "I found one!",
|
||||||
|
"EXIF:ImageDescription": "Girl holding pumpkin",
|
||||||
|
"XMP:Description": "Girl holding pumpkin",
|
||||||
|
"XMP:PersonInImage": "Katie",
|
||||||
|
"IPTC:Keywords": ["Kids", "keyword1", "keyword2", "subject1", "tagslist1"],
|
||||||
|
"XMP:TagsList": ["Kids", "keyword1", "keyword2", "subject1", "tagslist1"],
|
||||||
|
"XMP:Subject": ["Kids", "keyword1", "keyword2", "subject1", "tagslist1"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
CLI_EXIFTOOL_QUICKTIME = {
|
CLI_EXIFTOOL_QUICKTIME = {
|
||||||
"35329C57-B963-48D6-BB75-6AFF9370CBBC": {
|
"35329C57-B963-48D6-BB75-6AFF9370CBBC": {
|
||||||
"File:FileName": "Jellyfish.MOV",
|
"File:FileName": "Jellyfish.MOV",
|
||||||
@@ -1216,6 +1239,96 @@ def test_export_exiftool_option():
|
|||||||
assert "exiftool warning" not in result.output
|
assert "exiftool warning" not in result.output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(exiftool is None, reason="exiftool not installed")
|
||||||
|
def test_export_exiftool_merge():
|
||||||
|
""" test --exiftool-merge-keywords and --exiftool-merge-persons """
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
from osxphotos.__main__ import export
|
||||||
|
from osxphotos.exiftool import ExifTool
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
# pylint: disable=not-context-manager
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
for uuid in CLI_EXIFTOOL_MERGE:
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||||
|
".",
|
||||||
|
"-V",
|
||||||
|
"--exiftool",
|
||||||
|
"--uuid",
|
||||||
|
f"{uuid}",
|
||||||
|
"--exiftool-merge-keywords",
|
||||||
|
"--exiftool-merge-persons",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
files = glob.glob("*")
|
||||||
|
assert CLI_EXIFTOOL_MERGE[uuid]["File:FileName"] in files
|
||||||
|
|
||||||
|
exif = ExifTool(CLI_EXIFTOOL_MERGE[uuid]["File:FileName"]).asdict()
|
||||||
|
for key in CLI_EXIFTOOL_MERGE[uuid]:
|
||||||
|
if type(exif[key]) == list:
|
||||||
|
assert sorted(exif[key]) == sorted(CLI_EXIFTOOL_MERGE[uuid][key])
|
||||||
|
else:
|
||||||
|
assert exif[key] == CLI_EXIFTOOL_MERGE[uuid][key]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(exiftool is None, reason="exiftool not installed")
|
||||||
|
def test_export_exiftool_merge_sidecar():
|
||||||
|
""" test --exiftool-merge-keywords and --exiftool-merge-persons with --sidecar """
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
from osxphotos.__main__ import export
|
||||||
|
from osxphotos.exiftool import ExifTool
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
cwd = os.getcwd()
|
||||||
|
# pylint: disable=not-context-manager
|
||||||
|
with runner.isolated_filesystem():
|
||||||
|
for uuid in CLI_EXIFTOOL_MERGE:
|
||||||
|
result = runner.invoke(
|
||||||
|
export,
|
||||||
|
[
|
||||||
|
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||||
|
".",
|
||||||
|
"-V",
|
||||||
|
"--sidecar",
|
||||||
|
"json",
|
||||||
|
"--uuid",
|
||||||
|
f"{uuid}",
|
||||||
|
"--exiftool-merge-keywords",
|
||||||
|
"--exiftool-merge-persons",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
files = glob.glob("*")
|
||||||
|
json_file = f"{CLI_EXIFTOOL_MERGE[uuid]['File:FileName']}.json"
|
||||||
|
assert json_file in files
|
||||||
|
|
||||||
|
with open(json_file, "r") as fp:
|
||||||
|
exif = json.load(fp)[0]
|
||||||
|
|
||||||
|
for key in CLI_EXIFTOOL_MERGE[uuid]:
|
||||||
|
if key == "File:FileName":
|
||||||
|
continue
|
||||||
|
if type(exif[key]) == list:
|
||||||
|
expected = (
|
||||||
|
CLI_EXIFTOOL_MERGE[uuid][key]
|
||||||
|
if type(CLI_EXIFTOOL_MERGE[uuid][key]) == list
|
||||||
|
else [CLI_EXIFTOOL_MERGE[uuid][key]]
|
||||||
|
)
|
||||||
|
assert sorted(exif[key]) == sorted(expected)
|
||||||
|
else:
|
||||||
|
assert exif[key] == CLI_EXIFTOOL_MERGE[uuid][key]
|
||||||
|
|
||||||
|
|
||||||
def test_export_edited_suffix():
|
def test_export_edited_suffix():
|
||||||
""" test export with --edited-suffix """
|
""" test export with --edited-suffix """
|
||||||
import glob
|
import glob
|
||||||
|
|||||||
Reference in New Issue
Block a user