Fixed implementation of use_albums_as_keywords and use_persons_as_keywords, closes #115
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.28.7"
|
||||
__version__ = "0.28.8"
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user