Lots of work on export code

This commit is contained in:
Rhet Turnbull
2020-03-15 10:08:56 -07:00
parent c11afbaa6e
commit 0940f039d3
11 changed files with 370 additions and 238 deletions

View File

@@ -725,6 +725,7 @@ Export photo from the Photos library to another destination on disk.
- use_photos_export: boolean; (default=False), if True will attempt to export photo via applescript interaction with Photos; useful for forcing download of missing photos. This only works if the Photos library being used is the default library (last opened by Photos) as applescript will directly interact with whichever library Photos is currently using.
- 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
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
The json sidecar file can be used by exiftool to apply the metadata from the json file to the image. For example:
@@ -742,7 +743,6 @@ Then
If overwrite=False and increment=False, export will fail if destination file already exists
Returns the full path to the exported file
**Implementation Note**: Because the usual python file copy methods don't preserve all the metadata available on MacOS, export uses /usr/bin/ditto to do the copy for export. ditto preserves most metadata such as extended attributes, permissions, ACLs, etc.

View File

@@ -1344,7 +1344,7 @@ def export_photo(
overwrite=overwrite,
use_photos_export=use_photos_export,
exiftool=exiftool,
)
)[0]
# if export-edited, also export the edited version
# verify the photo has adjustments and valid path to avoid raising an exception

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.22.21"
__version__ = "0.22.23"

View File

@@ -505,7 +505,7 @@ class PhotoInfo:
):
""" export photo
dest: must be valid destination path (or exception raised)
filename: (optional): name of picture; if not provided, will use current filename
filename: (optional): name of exported picture; if not provided, will use current filename
**NOTE**: if provided, user must ensure file extension (suffix) is correct.
For example, if photo is .CR2 file, edited image may be .jpeg.
If you provide an extension different than what the actual file is,
@@ -525,12 +525,17 @@ class PhotoInfo:
use_photos_export: (boolean, default=False); if True will attempt to export photo via applescript interaction with Photos
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
returns the full path to the exported file """
returns list of full paths to the exported files """
# list of all files exported during this call to export
exported_files = []
logging.debug(f"dest ={dest}.filename={filename}")
# check edited and raise exception trying to export edited version of
# photo that hasn't been edited
if edited and not self.hasadjustments:
raise ValueError(
"Photo does not have adjustments, cannot export edited version"
)
# check arguments and get destination path and filename (if provided)
if filename and len(filename) > 2:
@@ -545,8 +550,8 @@ class PhotoInfo:
raise FileNotFoundError("Invalid path passed to export")
if filename and len(filename) == 1:
# if filename passed, use it, but verify extension
filename = filename[0]
# if filename passed, use it
fname = filename[0]
else:
# no filename provided so use the default
# if edited file requested, use filename but add _edited
@@ -560,25 +565,26 @@ class PhotoInfo:
)
edited_name = pathlib.Path(self.path_edited).name
edited_suffix = pathlib.Path(edited_name).suffix
filename = (
pathlib.Path(self.filename).stem + "_edited" + edited_suffix
)
fname = pathlib.Path(self.filename).stem + "_edited" + edited_suffix
else:
filename = self.filename
fname = self.filename
# check destination path
dest = pathlib.Path(dest)
filename = pathlib.Path(filename)
logging.debug(f"dest ={dest}.filename={filename}")
dest = dest / filename
fname = pathlib.Path(fname)
dest = dest / fname
# check extension of destination
if edited and self.path_edited is not None:
# use suffix from edited file
actual_suffix = pathlib.Path(self.path_edited).suffix
elif edited:
logging.warning("Invalid suffix check for missing edited file")
actual_suffix = ""
# use .jpeg as that's probably correct
# if edited and path_edited is None, will raise FileNotFoundError below
# unless use_photos_export is True
actual_suffix = ".jpeg"
else:
# use suffix from the non-edited file
actual_suffix = pathlib.Path(self.filename).suffix
if dest.suffix != actual_suffix:
@@ -613,16 +619,11 @@ class PhotoInfo:
# get path to source file and verify it's not None and is valid file
# TODO: how to handle ismissing or not hasadjustments and edited=True cases?
if edited:
if not self.hasadjustments:
logging.warning(
"Attempting to export edited photo but hasadjustments=False"
)
if self.path_edited is not None:
src = self.path_edited
else:
raise FileNotFoundError(
f"edited=True but path_edited is none; hasadjustments: {self.hasadjustments}"
f"Cannot export edited photo if path_edited is None"
)
else:
if self.ismissing:
@@ -630,13 +631,10 @@ class PhotoInfo:
f"Attempting to export photo with ismissing=True: path = {self.path}"
)
if self.path is None:
logging.warning(
f"Attempting to export photo but path is None: ismissing = {self.ismissing}"
)
raise FileNotFoundError("Cannot export photo if path is None")
else:
if self.path is not None:
src = self.path
else:
raise FileNotFoundError("Cannot export photo if path is None")
if not os.path.isfile(src):
raise FileNotFoundError(f"{src} does not appear to exist")
@@ -675,16 +673,18 @@ class PhotoInfo:
else:
# didn't get passed a filename, add _edited
filestem = f"{dest.stem}_edited"
exported = _export_photo_uuid_applescript(
self.uuid,
dest.parent,
filestem=filestem,
original=False,
edited=True,
live_photo=live_photo,
timeout=timeout,
burst=self.burst,
)
dest = dest.parent / f"{filestem}.jpeg"
exported = _export_photo_uuid_applescript(
self.uuid,
dest.parent,
filestem=filestem,
original=False,
edited=True,
live_photo=live_photo,
timeout=timeout,
burst=self.burst,
)
else:
# export original version and not edited
filestem = dest.stem
@@ -702,7 +702,9 @@ class PhotoInfo:
if exported is not None:
exported_files.extend(exported)
else:
logging.warning(f"Error exporting photo {self.uuid} to {dest}")
logging.warning(
f"Error exporting photo {self.uuid} to {dest} with use_photos_export"
)
if sidecar_json:
logging.debug("writing exiftool_json_sidecar")
@@ -724,14 +726,12 @@ class PhotoInfo:
logging.warning(f"Error writing xmp sidecar to {sidecar_filename}")
raise e
logging.debug(f"export exported_files: {exported_files}")
# if exiftool, write the metadata
if exiftool and exported_files:
for exported_file in exported_files:
self._write_exif_data(exported_file)
return str(dest)
return exported_files
def _write_exif_data(self, filepath):
""" write exif data to image file at filepath
@@ -771,7 +771,6 @@ class PhotoInfo:
exif = {}
exif["_CreatedBy"] = "osxphotos, https://github.com/RhetTbull/osxphotos"
exif["File:FileName"] = self.filename
if self.description:
exif["EXIF:ImageDescription"] = self.description

View File

@@ -255,7 +255,7 @@ def list_photo_libraries():
# On older MacOS versions, mdfind appears to ignore some libraries
# glob to find libraries in ~/Pictures then mdfind to find all the others
# TODO: make this more robust
lib_list = glob.glob(f"{str(Path.home())}/Pictures/*.photoslibrary")
lib_list = glob.glob(f"{str(pathlib.Path.home())}/Pictures/*.photoslibrary")
# On older OS, may not get all libraries so make sure we get the last one
last_lib = get_last_library_path()
@@ -327,7 +327,8 @@ def _export_photo_uuid_applescript(
If filestem.ext exists, it wil be overwritten
original: (boolean) if True, export original image; default = True
edited: (boolean) if True, export edited photo; default = False
will produce an error if image does not have edits/adjustments
If photo not edited and edited=True, will still export the original image
caller must verify image has been edited
*Note*: must be called with either edited or original but not both,
will raise error if called with both edited and original = True
live_photo: (boolean) if True, export associated .mov live photo; default = False
@@ -448,7 +449,7 @@ def _db_is_locked(dbname):
conn.close()
logging.debug(f"{dbname} is not locked")
locked = False
except Exception as e:
except:
logging.debug(f"{dbname} is locked")
locked = True

View File

@@ -415,20 +415,18 @@ def test_export_1():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest)
got_dest = photos[0].export(dest)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
# remove the temporary file
os.remove(got_dest)
def test_export_2():
# test export with user provided filename
@@ -439,21 +437,19 @@ def test_export_2():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
timestamp = time.time()
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename)
got_dest = photos[0].export(dest, filename)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
# remove the temporary file
os.remove(got_dest)
def test_export_3():
# test file already exists and test increment=True (default)
@@ -464,7 +460,8 @@ def test_export_3():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
@@ -473,16 +470,12 @@ def test_export_3():
filename2 = f"{filename2.stem} (1){filename2.suffix}"
expected_dest_2 = os.path.join(dest, filename2)
got_dest = photos[0].export(dest)
got_dest_2 = photos[0].export(dest)
got_dest = photos[0].export(dest)[0]
got_dest_2 = photos[0].export(dest)[0]
assert got_dest_2 == expected_dest_2
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
os.remove(got_dest_2)
def test_export_4():
# test user supplied file already exists and test increment=True (default)
@@ -494,7 +487,8 @@ def test_export_4():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
@@ -503,16 +497,12 @@ def test_export_4():
filename2 = f"osxphotos-export-2-test-{timestamp} (1).jpg"
expected_dest_2 = os.path.join(dest, filename2)
got_dest = photos[0].export(dest, filename)
got_dest_2 = photos[0].export(dest, filename)
got_dest = photos[0].export(dest, filename)[0]
got_dest_2 = photos[0].export(dest, filename)[0]
assert got_dest_2 == expected_dest_2
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
os.remove(got_dest_2)
def test_export_5():
# test file already exists and test increment=True (default)
@@ -523,23 +513,21 @@ def test_export_5():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest)
got_dest_2 = photos[0].export(dest, overwrite=True)
got_dest = photos[0].export(dest)[0]
got_dest_2 = photos[0].export(dest, overwrite=True)[0]
assert got_dest_2 == got_dest
assert got_dest_2 == expected_dest
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
def test_export_6():
# test user supplied file already exists and test increment=True (default)
@@ -552,7 +540,8 @@ def test_export_6():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
@@ -560,16 +549,13 @@ def test_export_6():
filename = f"osxphotos-export-test-{timestamp}.jpg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename)
got_dest_2 = photos[0].export(dest, filename, overwrite=True)
got_dest = photos[0].export(dest, filename)[0]
got_dest_2 = photos[0].export(dest, filename, overwrite=True)[0]
assert got_dest_2 == got_dest
assert got_dest_2 == expected_dest
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
def test_export_7():
# test file already exists and test increment=False (not default), overwrite=False (default)
@@ -580,21 +566,19 @@ def test_export_7():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
filename = photos[0].filename
got_dest = photos[0].export(dest)
got_dest = photos[0].export(dest)[0]
with pytest.raises(Exception) as e:
# try to export again with increment = False
assert photos[0].export(dest, increment=False)
assert e.type == type(FileExistsError())
# remove the temporary file
os.remove(got_dest)
def test_export_8():
# try to export missing file
@@ -605,14 +589,15 @@ def test_export_8():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
filename = photos[0].filename
with pytest.raises(Exception) as e:
assert photos[0].export(dest)
assert photos[0].export(dest)[0]
assert e.type == type(FileNotFoundError())
@@ -625,15 +610,16 @@ def test_export_9():
import osxphotos
dest = tempfile.gettempdir()
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
filename = photos[0].filename
with pytest.raises(Exception) as e:
assert photos[0].export(dest, edited=True)
assert e.type == type(FileNotFoundError())
assert e.type == ValueError
def test_export_10():
@@ -646,8 +632,9 @@ def test_export_10():
import osxphotos
dest = tempfile.gettempdir()
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
timestamp = time.time()
@@ -655,7 +642,7 @@ def test_export_10():
with pytest.raises(Exception) as e:
assert photos[0].export(dest, filename, edited=True)
assert e.type == type(FileNotFoundError())
assert e.type == ValueError
def test_export_11():
@@ -667,7 +654,8 @@ def test_export_11():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
@@ -675,12 +663,9 @@ def test_export_11():
filename = f"osxphotos-export-test-{timestamp}.jpg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename, edited=True)
got_dest = photos[0].export(dest, filename, edited=True)[0]
assert got_dest == expected_dest
# remove the temporary file
os.remove(got_dest)
def test_export_12():
# export edited file with default name
@@ -691,7 +676,8 @@ def test_export_12():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
@@ -700,12 +686,9 @@ def test_export_12():
filename = pathlib.Path(photos[0].filename).stem + "_edited" + edited_suffix
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, edited=True)
got_dest = photos[0].export(dest, edited=True)[0]
assert got_dest == expected_dest
# remove the temporary file
os.remove(got_dest)
def test_export_13():
# export to invalid destination
@@ -716,7 +699,8 @@ def test_export_13():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
# create a folder that doesn't exist
i = 0

View File

@@ -67,20 +67,18 @@ def test_export_1():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest)
got_dest = photos[0].export(dest)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
# remove the temporary file
os.remove(got_dest)
def test_export_2():
# test export with user provided filename
@@ -91,21 +89,19 @@ def test_export_2():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
timestamp = time.time()
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename)
got_dest = photos[0].export(dest, filename)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
# remove the temporary file
os.remove(got_dest)
def test_export_3():
# test file already exists and test increment=True (default)
@@ -116,7 +112,8 @@ def test_export_3():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
@@ -126,16 +123,12 @@ def test_export_3():
expected_dest = os.path.join(dest, filename)
expected_dest_2 = os.path.join(dest, filename2)
got_dest = photos[0].export(dest)
got_dest_2 = photos[0].export(dest)
got_dest = photos[0].export(dest)[0]
got_dest_2 = photos[0].export(dest)[0]
assert got_dest_2 == expected_dest_2
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
os.remove(got_dest_2)
def test_export_4():
# test user supplied file already exists and test increment=True (default)
@@ -147,7 +140,8 @@ def test_export_4():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
@@ -157,16 +151,12 @@ def test_export_4():
expected_dest = os.path.join(dest, filename)
expected_dest_2 = os.path.join(dest, filename2)
got_dest = photos[0].export(dest, filename)
got_dest_2 = photos[0].export(dest, filename)
got_dest = photos[0].export(dest, filename)[0]
got_dest_2 = photos[0].export(dest, filename)[0]
assert got_dest_2 == expected_dest_2
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
os.remove(got_dest_2)
def test_export_5():
# test file already exists and test increment=True (default)
@@ -177,23 +167,21 @@ def test_export_5():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest)
got_dest_2 = photos[0].export(dest, overwrite=True)
got_dest = photos[0].export(dest)[0]
got_dest_2 = photos[0].export(dest, overwrite=True)[0]
assert got_dest_2 == got_dest
assert got_dest_2 == expected_dest
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
def test_export_6():
# test user supplied file already exists and test increment=True (default)
@@ -206,7 +194,8 @@ def test_export_6():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
@@ -214,16 +203,13 @@ def test_export_6():
filename = f"osxphotos-export-test-{timestamp}.jpg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename)
got_dest_2 = photos[0].export(dest, filename, overwrite=True)
got_dest = photos[0].export(dest, filename)[0]
got_dest_2 = photos[0].export(dest, filename, overwrite=True)[0]
assert got_dest_2 == got_dest
assert got_dest_2 == expected_dest
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
def test_export_7():
# test file already exists and test increment=False (not default), overwrite=False (default)
@@ -234,22 +220,20 @@ def test_export_7():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest)
got_dest = photos[0].export(dest)[0]
with pytest.raises(Exception) as e:
# try to export again with increment = False
assert photos[0].export(dest, increment=False)
assert e.type == type(FileExistsError())
# remove the temporary file
os.remove(got_dest)
def test_export_8():
# try to export missing file
@@ -260,7 +244,8 @@ def test_export_8():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
@@ -281,16 +266,14 @@ def test_export_9():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
with pytest.raises(Exception) as e:
assert photos[0].export(dest, edited=True)
assert e.type == type(FileNotFoundError())
assert e.type == ValueError
def test_export_10():
@@ -303,7 +286,8 @@ def test_export_10():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
@@ -313,7 +297,7 @@ def test_export_10():
with pytest.raises(Exception) as e:
assert photos[0].export(dest, filename, edited=True)
assert e.type == type(FileNotFoundError())
assert e.type == ValueError
def test_export_11():
@@ -325,7 +309,8 @@ def test_export_11():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
@@ -333,12 +318,9 @@ def test_export_11():
filename = f"osxphotos-export-test-{timestamp}.jpg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename, edited=True)
got_dest = photos[0].export(dest, filename, edited=True)[0]
assert got_dest == expected_dest
# remove the temporary file
os.remove(got_dest)
def test_export_12():
# export edited file with default name
@@ -349,7 +331,8 @@ def test_export_12():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
@@ -358,12 +341,9 @@ def test_export_12():
filename = pathlib.Path(photos[0].filename).stem + "_edited" + edited_suffix
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, edited=True)
got_dest = photos[0].export(dest, edited=True)[0]
assert got_dest == expected_dest
# remove the temporary file
os.remove(got_dest)
def test_export_13():
# export to invalid destination
@@ -374,7 +354,8 @@ def test_export_13():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
# create a folder that doesn't exist
i = 0
@@ -446,8 +427,7 @@ def test_exiftool_json_sidecar():
json_expected = json.loads(
"""
[{"File:FileName": "DC99FBDD-7A52-4100-A5BB-344131646C30.jpeg",
"XMP:Title": "St. James\'s Park",
[{"XMP:Title": "St. James\'s Park",
"XMP:TagsList": ["London 2018", "St. James\'s Park", "England", "United Kingdom", "UK", "London"],
"IPTC:Keywords": ["London 2018", "St. James\'s Park", "England", "United Kingdom", "UK", "London"],
"XMP:Subject": ["London 2018", "St. James\'s Park", "England", "United Kingdom", "UK", "London"],

View File

@@ -0,0 +1,158 @@
import os
import pytest
from osxphotos._constants import _UNKNOWN_PERSON
skip_test = False if "OSXPHOTOS_TEST_EXPORT" in os.environ else True
pytestmark = pytest.mark.skipif(
skip_test, reason="These tests only run against system photos library"
)
PHOTOS_DB = "/Users/rhet/Pictures/Photos Library.photoslibrary"
UUID_DICT = {
"has_adjustments": "A8111956-E900-4DEC-9191-A04A87C07BC5",
"no_adjustments": "EA7BB55F-92F1-4818-94E3-E8DEDC6B2E31",
"live": "9032C168-9319-40C0-8210-5ADC42F4C603",
}
@pytest.fixture(scope="module")
def photosdb():
import osxphotos
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
return photosdb
def test_export_default_name(photosdb):
# test basic export
# get an unedited image and export it using default filename
import os
import os.path
import tempfile
import osxphotos
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, use_photos_export=True)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
def test_export_supplied_name(photosdb):
# test export with user provided filename
import os
import os.path
import tempfile
import time
import osxphotos
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
timestamp = time.time()
filename = f"osxphotos-export-2-test-{timestamp}.jpeg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename, use_photos_export=True)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
def test_export_edited(photosdb):
# test export edited file
import os
import os.path
import pathlib
import tempfile
import osxphotos
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
suffix = pathlib.Path(photos[0].path_edited).suffix
filename = f"{pathlib.Path(photos[0].filename).stem}_edited{suffix}"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, use_photos_export=True, edited=True)[0]
assert got_dest == expected_dest
assert os.path.isfile(expected_dest)
def test_export_edited_exiftool(photosdb):
# test export edited file
import os
import os.path
import pathlib
import tempfile
import osxphotos
import osxphotos.exiftool
import logging
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
got_dest = photos[0].export(
dest, use_photos_export=True, edited=True, exiftool=True
)
logging.warning(got_dest)
got_dest = got_dest[0]
assert os.path.isfile(got_dest)
exif = osxphotos.exiftool.ExifTool(got_dest)
assert exif.data["IPTC:Keywords"] == "osxphotos"
def test_export_edited_supplied_name(photosdb):
# test export with user provided filename
import os
import os.path
import tempfile
import time
import osxphotos
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
timestamp = time.time()
filename = f"osxphotos-export-2-test-{timestamp}.jpeg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename, use_photos_export=True, edited=True)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
def test_export_edited_no_edit(photosdb):
# test export edited file if not actually edited
import os
import os.path
import pathlib
import tempfile
import osxphotos
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
with pytest.raises(Exception) as e:
assert photos[0].export(dest, use_photos_export=True, edited=True)
assert e.type == ValueError

View File

@@ -55,20 +55,18 @@ def test_export_1():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest)
got_dest = photos[0].export(dest)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
# remove the temporary file
os.remove(got_dest)
def test_export_2():
# test export with user provided filename
@@ -79,21 +77,19 @@ def test_export_2():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
timestamp = time.time()
filename = f"osxphotos-export-2-test-{timestamp}.jpg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename)
got_dest = photos[0].export(dest, filename)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
# remove the temporary file
os.remove(got_dest)
def test_export_3():
# test file already exists and test increment=True (default)
@@ -104,7 +100,8 @@ def test_export_3():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
@@ -114,16 +111,12 @@ def test_export_3():
expected_dest = os.path.join(dest, filename)
expected_dest_2 = os.path.join(dest, filename2)
got_dest = photos[0].export(dest)
got_dest_2 = photos[0].export(dest)
got_dest = photos[0].export(dest)[0]
got_dest_2 = photos[0].export(dest)[0]
assert got_dest_2 == expected_dest_2
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
os.remove(got_dest_2)
def test_export_4():
# test user supplied file already exists and test increment=True (default)
@@ -135,7 +128,8 @@ def test_export_4():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
@@ -145,16 +139,12 @@ def test_export_4():
expected_dest = os.path.join(dest, filename)
expected_dest_2 = os.path.join(dest, filename2)
got_dest = photos[0].export(dest, filename)
got_dest_2 = photos[0].export(dest, filename)
got_dest = photos[0].export(dest, filename)[0]
got_dest_2 = photos[0].export(dest, filename)[0]
assert got_dest_2 == expected_dest_2
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
os.remove(got_dest_2)
def test_export_5():
# test file already exists and test increment=True (default)
@@ -165,23 +155,21 @@ def test_export_5():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest)
got_dest_2 = photos[0].export(dest, overwrite=True)
got_dest = photos[0].export(dest)[0]
got_dest_2 = photos[0].export(dest, overwrite=True)[0]
assert got_dest_2 == got_dest
assert got_dest_2 == expected_dest
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
def test_export_6():
# test user supplied file already exists and test increment=True (default)
@@ -194,7 +182,8 @@ def test_export_6():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
@@ -202,16 +191,13 @@ def test_export_6():
filename = f"osxphotos-export-test-{timestamp}.jpg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename)
got_dest_2 = photos[0].export(dest, filename, overwrite=True)
got_dest = photos[0].export(dest, filename)[0]
got_dest_2 = photos[0].export(dest, filename, overwrite=True)[0]
assert got_dest_2 == got_dest
assert got_dest_2 == expected_dest
assert os.path.isfile(got_dest_2)
# remove the temporary file
os.remove(got_dest)
def test_export_7():
# test file already exists and test increment=False (not default), overwrite=False (default)
@@ -222,22 +208,20 @@ def test_export_7():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["export"]])
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest)
got_dest = photos[0].export(dest)[0]
with pytest.raises(Exception) as e:
# try to export again with increment = False
assert photos[0].export(dest, increment=False)
assert photos[0].export(dest, increment=False)[0]
assert e.type == type(FileExistsError())
# remove the temporary file
os.remove(got_dest)
def test_export_8():
# try to export missing file
@@ -248,7 +232,8 @@ def test_export_8():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["missing"]])
@@ -256,7 +241,7 @@ def test_export_8():
expected_dest = os.path.join(dest, filename)
with pytest.raises(Exception) as e:
assert photos[0].export(dest)
assert photos[0].export(dest)[0]
assert e.type == type(FileNotFoundError())
@@ -269,7 +254,8 @@ def test_export_9():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
@@ -278,7 +264,7 @@ def test_export_9():
with pytest.raises(Exception) as e:
assert photos[0].export(dest, edited=True)
assert e.type == type(FileNotFoundError())
assert e.type == ValueError
def test_export_10():
@@ -291,7 +277,8 @@ def test_export_10():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["no_adjustments"]])
@@ -300,8 +287,8 @@ def test_export_10():
expected_dest = os.path.join(dest, filename)
with pytest.raises(Exception) as e:
assert photos[0].export(dest, filename, edited=True)
assert e.type == type(FileNotFoundError())
assert photos[0].export(dest, filename, edited=True)[0]
assert e.type == ValueError
def test_export_11():
@@ -313,7 +300,8 @@ def test_export_11():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
@@ -321,12 +309,9 @@ def test_export_11():
filename = f"osxphotos-export-test-{timestamp}.jpg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename, edited=True)
got_dest = photos[0].export(dest, filename, edited=True)[0]
assert got_dest == expected_dest
# remove the temporary file
os.remove(got_dest)
def test_export_12():
# export edited file with default name
@@ -337,7 +322,8 @@ def test_export_12():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
@@ -346,12 +332,9 @@ def test_export_12():
filename = pathlib.Path(photos[0].filename).stem + "_edited" + edited_suffix
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, edited=True)
got_dest = photos[0].export(dest, edited=True)[0]
assert got_dest == expected_dest
# remove the temporary file
os.remove(got_dest)
def test_export_13():
# export to invalid destination
@@ -362,7 +345,8 @@ def test_export_13():
import osxphotos
dest = tempfile.gettempdir()
tempdir = tempfile.TemporaryDirectory(prefix="osxphotos_")
dest = tempdir.name
# create a folder that doesn't exist
i = 0
@@ -377,7 +361,7 @@ def test_export_13():
expected_dest = os.path.join(dest, filename)
with pytest.raises(Exception) as e:
assert photos[0].export(dest)
assert photos[0].export(dest)[0]
assert e.type == type(FileNotFoundError())
@@ -390,8 +374,7 @@ def test_exiftool_json_sidecar():
json_expected = json.loads(
"""
[{"File:FileName": "St James Park.jpg",
"XMP:Title": "St. James\'s Park",
[{"XMP:Title": "St. James\'s Park",
"XMP:TagsList": ["London 2018", "St. James\'s Park", "England", "United Kingdom", "UK", "London"],
"IPTC:Keywords": ["London 2018", "St. James\'s Park", "England", "United Kingdom", "UK", "London"],
"XMP:Subject": ["London 2018", "St. James\'s Park", "England", "United Kingdom", "UK", "London"],

View File

@@ -31,7 +31,7 @@ def test_export_1():
filename = photos[0].filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest)
got_dest = photos[0].export(dest)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
@@ -55,7 +55,7 @@ def test_export_2():
filename = photos[0].original_filename
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename)
got_dest = photos[0].export(dest, filename)[0]
assert got_dest == expected_dest
assert os.path.isfile(got_dest)
@@ -81,7 +81,7 @@ def test_export_edited_name():
filename = f"osxphotos-export-test-{timestamp}.jpg"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename, edited=True)
got_dest = photos[0].export(dest, filename, edited=True)[0]
assert got_dest == expected_dest
assert pathlib.Path(got_dest).name == filename
@@ -100,7 +100,7 @@ def test_export_edited_default():
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["has_adjustments"]])
got_dest = photos[0].export(dest, edited=True)
got_dest = photos[0].export(dest, edited=True)[0]
assert pathlib.Path(got_dest).name == FILENAME_DICT["current_edited"]
@@ -125,7 +125,7 @@ def test_export_edited_wrong_suffix(caplog):
filename = f"osxphotos-export-test-{timestamp}.cr2"
expected_dest = os.path.join(dest, filename)
got_dest = photos[0].export(dest, filename, edited=True)
got_dest = photos[0].export(dest, filename, edited=True)[0]
assert "Invalid destination suffix" in caplog.text
assert got_dest == expected_dest
assert pathlib.Path(got_dest).name == filename

View File

@@ -46,9 +46,8 @@ def test_export_live_1():
filename = photos[0].filename
expected_dest = os.path.join(dest.name, filename)
got_dest = photos[0].export(dest.name, live_photo=True)
got_dest = photos[0].export(dest.name, live_photo=True)[0]
got_movie = f"{pathlib.Path(got_dest).parent / pathlib.Path(got_dest).stem}.mov"
expected_dest = os.path.join(dest.name, filename)
files = glob.glob(os.path.join(dest.name, "*"))
assert len(files) == 2
@@ -73,7 +72,7 @@ def test_export_live_2():
filename = photos[0].filename
expected_dest = os.path.join(dest.name, filename)
got_dest = photos[0].export(dest.name, live_photo=False)
got_dest = photos[0].export(dest.name, live_photo=False)[0]
got_movie = f"{pathlib.Path(got_dest).parent / pathlib.Path(got_dest).stem}.mov"
files = glob.glob(os.path.join(dest.name, "*"))
@@ -83,6 +82,34 @@ def test_export_live_2():
assert got_movie not in files
def test_export_live_3():
# export a live photo and associated .mov,
# check list return of export
import glob
import os.path
import pathlib
import tempfile
import osxphotos
dest = tempfile.TemporaryDirectory(prefix="osxphotos_")
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
photos = photosdb.photos(uuid=[UUID_DICT["live"]])
filename = photos[0].filename
expected_dest = os.path.join(dest.name, filename)
expected_mov = f"{dest.name}/{pathlib.Path(expected_dest).stem}.mov"
got_files = photos[0].export(dest.name, live_photo=True)
# got_dest = got_files[0]
# got_movie = f"{pathlib.Path(got_dest).parent / pathlib.Path(got_dest).stem}.mov"
# files = glob.glob(os.path.join(dest.name, "*"))
assert len(got_files) == 2
assert expected_dest in got_files
assert expected_mov in got_files
# def test_export_live_3():
# # export a live photo and associated .mov and edited file
# import glob