Fixed implementation of use_albums_as_keywords and use_persons_as_keywords, closes #115

This commit is contained in:
Rhet Turnbull
2020-04-28 07:41:37 -07:00
parent a80071111f
commit 1ceda15134
5 changed files with 65 additions and 37 deletions

View File

@@ -963,7 +963,7 @@ Returns True if photo is a panorama, otherwise False.
Returns a JSON representation of all photo info
#### `export()`
`export(dest, *filename, edited=False, live_photo=False, overwrite=False, increment=True, sidecar_json=False, sidecar_xmp=False, use_photos_export=False, timeout=120, exiftool=False, no_xattr=False)`
`export(dest, *filename, edited=False, live_photo=False, overwrite=False, increment=True, sidecar_json=False, sidecar_xmp=False, use_photos_export=False, timeout=120, exiftool=False, no_xattr=False, use_albums_as_keywords=False, use_persons_as_keywords=False)`
Export photo from the Photos library to another destination on disk.
- dest: must be valid destination path as str (or exception raised).
@@ -978,6 +978,8 @@ Export photo from the Photos library to another destination on disk.
- timeout: (int, default=120) timeout in seconds used with use_photos_export
- exiftool: (boolean, default = False) if True, will use [exiftool](https://exiftool.org/) to write metadata directly to the exported photo; exiftool must be installed and in the system path
- no_xattr: (boolean, default = False); if True, exports file without preserving extended attributes
- use_albums_as_keywords: (boolean, default = False); if True, will use album names as keywords when exporting metadata with exiftool or sidecar
- use_persons_as_keywords: (boolean, default = False); if True, will use person names as keywords when exporting metadata with exiftool or sidecar
Returns: list of paths to exported files. More than one file could be exported, for example if live_photo=True, both the original imaage and the associated .mov file will be exported

View File

@@ -895,13 +895,13 @@ def query(
)
@click.option(
"--person-keyword",
is_flag = True,
help = "Use person in image as keyword/tag when exporting metadata."
is_flag=True,
help="Use person in image as keyword/tag when exporting metadata.",
)
@click.option(
"--album-keyword",
is_flag = True,
help = "Use album name as keyword/tag when exporting metadata."
is_flag=True,
help="Use album name as keyword/tag when exporting metadata.",
)
@click.option(
"--current-name",
@@ -1157,16 +1157,6 @@ def export(
)
if photos:
# set the persons_as_keywords and albums_as_keywords on the database object
# to control metadata export
photosdb = photos[0]._db
if person_keyword:
# add persons as keywords
photosdb.use_persons_as_keywords = True
if album_keyword:
# add albums as keywords
photosdb.use_albums_as_keywords = True
if export_bursts:
# add the burst_photos to the export set
photos_burst = [p for p in photos if p.burst]
@@ -1196,6 +1186,8 @@ def export(
directory,
no_extended_attributes,
export_raw,
album_keyword,
person_keyword,
)
else:
for p in photos:
@@ -1214,6 +1206,8 @@ def export(
directory,
no_extended_attributes,
export_raw,
album_keyword,
person_keyword,
)
if export_paths:
click.echo(f"Exported {p.filename} to {export_paths}")
@@ -1600,6 +1594,8 @@ def export_photo(
directory,
no_extended_attributes,
export_raw,
album_keyword,
person_keyword,
):
""" Helper function for export that does the actual export
photo: PhotoInfo object
@@ -1616,6 +1612,8 @@ def export_photo(
directory: template used to determine output directory
no_extended_attributes: boolean; if True, exports photo without preserving extended attributes
export_raw: boolean; if True exports RAW image associate with the photo
album_keyword: boolean; if True, exports album names as keywords in metadata
person_keyword: boolean; if True, exports person names as keywords in metadata
returns list of path(s) of exported photo or None if photo was missing
"""
@@ -1699,6 +1697,8 @@ def export_photo(
use_photos_export=use_photos_export,
exiftool=exiftool,
no_xattr=no_extended_attributes,
use_albums_as_keywords=album_keyword,
use_persons_as_keywords=person_keyword,
)[0]
photo_paths.append(photo_path)
@@ -1734,6 +1734,8 @@ def export_photo(
use_photos_export=use_photos_export,
exiftool=exiftool,
no_xattr=no_extended_attributes,
use_albums_as_keywords=album_keyword,
use_persons_as_keywords=person_keyword,
)
return photo_paths

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.28.7"
__version__ = "0.28.8"

View File

@@ -634,6 +634,8 @@ class PhotoInfo:
timeout=120,
exiftool=False,
no_xattr=False,
use_albums_as_keywords=False,
use_persons_as_keywords=False,
):
""" export photo
dest: must be valid destination path (or exception raised)
@@ -659,7 +661,12 @@ class PhotoInfo:
timeout: (int, default=120) timeout in seconds used with use_photos_export
exiftool: (boolean, default = False); if True, will use exiftool to write metadata to export file
no_xattr: (boolean, default = False); if True, exports file without preserving extended attributes
returns list of full paths to the exported files """
returns list of full paths to the exported files
use_albums_as_keywords: (boolean, default = False); if True, will include album names in keywords
when exporting metadata with exiftool or sidecar
use_persons_as_keywords: (boolean, default = False); if True, will include person names in keywords
when exporting metadata with exiftool or sidecar
"""
# list of all files exported during this call to export
exported_files = []
@@ -863,7 +870,10 @@ class PhotoInfo:
if sidecar_json:
logging.debug("writing exiftool_json_sidecar")
sidecar_filename = dest.parent / pathlib.Path(f"{dest.stem}.json")
sidecar_str = self._exiftool_json_sidecar()
sidecar_str = self._exiftool_json_sidecar(
use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords,
)
try:
self._write_sidecar(sidecar_filename, sidecar_str)
except Exception as e:
@@ -873,7 +883,10 @@ class PhotoInfo:
if sidecar_xmp:
logging.debug("writing xmp_sidecar")
sidecar_filename = dest.parent / pathlib.Path(f"{dest.stem}.xmp")
sidecar_str = self._xmp_sidecar()
sidecar_str = self._xmp_sidecar(
use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords,
)
try:
self._write_sidecar(sidecar_filename, sidecar_str)
except Exception as e:
@@ -883,17 +896,28 @@ class PhotoInfo:
# if exiftool, write the metadata
if exiftool and exported_files:
for exported_file in exported_files:
self._write_exif_data(exported_file)
self._write_exif_data(
exported_file,
use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords,
)
return exported_files
def _write_exif_data(self, filepath):
def _write_exif_data(
self, filepath, use_albums_as_keywords=False, use_persons_as_keywords=False
):
""" write exif data to image file at filepath
filepath: full path to the image file """
if not os.path.exists(filepath):
raise FileNotFoundError(f"Could not find file {filepath}")
exiftool = ExifTool(filepath)
exif_info = json.loads(self._exiftool_json_sidecar())[0]
exif_info = json.loads(
self._exiftool_json_sidecar(
use_albums_as_keywords=use_albums_as_keywords,
use_persons_as_keywords=use_persons_as_keywords,
)
)[0]
for exiftag, val in exif_info.items():
if type(val) == list:
# more than one, set first value the add additional values
@@ -904,7 +928,9 @@ class PhotoInfo:
else:
exiftool.setvalue(exiftag, val)
def _exiftool_json_sidecar(self):
def _exiftool_json_sidecar(
self, use_albums_as_keywords=False, use_persons_as_keywords=False
):
""" 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
Exports the following:
@@ -942,10 +968,10 @@ class PhotoInfo:
# filter out _UNKNOWN_PERSON
person_list = [p for p in self.persons if p != _UNKNOWN_PERSON]
if self._db.use_persons_as_keywords and person_list:
if use_persons_as_keywords and person_list:
keyword_list.extend(person_list)
if self._db.use_albums_as_keywords and self.albums:
if use_albums_as_keywords and self.albums:
keyword_list.extend(self.albums)
if keyword_list:
@@ -990,7 +1016,7 @@ class PhotoInfo:
json_str = json.dumps([exif])
return json_str
def _xmp_sidecar(self):
def _xmp_sidecar(self, use_albums_as_keywords=False, use_persons_as_keywords=False):
""" returns string for XMP sidecar """
# TODO: add additional fields to XMP file?
@@ -1007,10 +1033,10 @@ class PhotoInfo:
# filter out _UNKNOWN_PERSON
person_list = [p for p in self.persons if p != _UNKNOWN_PERSON]
if self._db.use_persons_as_keywords and person_list:
if use_persons_as_keywords and person_list:
keyword_list.extend(person_list)
if self._db.use_albums_as_keywords and self.albums:
if use_albums_as_keywords and self.albums:
keyword_list.extend(self.albums)
subject_list = []

View File

@@ -487,7 +487,6 @@ def test_exiftool_json_sidecar_use_persons_keyword():
import json
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb.use_persons_as_keywords = True
photos = photosdb.photos(uuid=[UUID_DICT["xmp"]])
json_expected = json.loads(
@@ -506,7 +505,7 @@ def test_exiftool_json_sidecar_use_persons_keyword():
"""
)[0]
json_got = photos[0]._exiftool_json_sidecar()
json_got = photos[0]._exiftool_json_sidecar(use_persons_as_keywords=True)
json_got = json.loads(json_got)[0]
# some gymnastics to account for different sort order in different pythons
@@ -529,7 +528,6 @@ def test_exiftool_json_sidecar_use_albums_keyword():
import json
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb.use_albums_as_keywords = True
photos = photosdb.photos(uuid=[UUID_DICT["xmp"]])
json_expected = json.loads(
@@ -548,7 +546,7 @@ def test_exiftool_json_sidecar_use_albums_keyword():
"""
)[0]
json_got = photos[0]._exiftool_json_sidecar()
json_got = photos[0]._exiftool_json_sidecar(use_albums_as_keywords=True)
json_got = json.loads(json_got)[0]
# some gymnastics to account for different sort order in different pythons
@@ -624,11 +622,11 @@ def test_xmp_sidecar():
for line_expected, line_got in zip(xmp_expected_lines, xmp_got_lines):
assert line_expected == line_got
def test_xmp_sidecar_use_persons_keyword():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb.use_persons_as_keywords = True
photos = photosdb.photos(uuid=[UUID_DICT["xmp"]])
xmp_expected = """<!-- Created with osxphotos https://github.com/RhetTbull/osxphotos -->
@@ -679,17 +677,17 @@ def test_xmp_sidecar_use_persons_keyword():
xmp_expected_lines = [line.strip() for line in xmp_expected.split("\n")]
xmp_got = photos[0]._xmp_sidecar()
xmp_got = photos[0]._xmp_sidecar(use_persons_as_keywords=True)
xmp_got_lines = [line.strip() for line in xmp_got.split("\n")]
for line_expected, line_got in zip(xmp_expected_lines, xmp_got_lines):
assert line_expected == line_got
def test_xmp_sidecar_use_albums_keyword():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photosdb.use_albums_as_keywords = True
photos = photosdb.photos(uuid=[UUID_DICT["xmp"]])
xmp_expected = """<!-- Created with osxphotos https://github.com/RhetTbull/osxphotos -->
@@ -740,8 +738,8 @@ def test_xmp_sidecar_use_albums_keyword():
xmp_expected_lines = [line.strip() for line in xmp_expected.split("\n")]
xmp_got = photos[0]._xmp_sidecar()
xmp_got = photos[0]._xmp_sidecar(use_albums_as_keywords=True)
xmp_got_lines = [line.strip() for line in xmp_got.split("\n")]
for line_expected, line_got in zip(xmp_expected_lines, xmp_got_lines):
assert line_expected == line_got
assert line_expected == line_got