diff --git a/osxphotos/__main__.py b/osxphotos/__main__.py
index ca32fc13..51e4c414 100644
--- a/osxphotos/__main__.py
+++ b/osxphotos/__main__.py
@@ -629,10 +629,10 @@ def query(
help="Create sidecar for each photo exported; valid FORMAT values: xmp, json; "
f"--sidecar json: create JSON sidecar useable by exiftool ({_EXIF_TOOL_URL}) "
"The sidecar file can be used to apply metadata to the file with exiftool, for example: "
- '"exiftool -j=photoname.jpg.json photoname.jpg" '
- "The sidecar file is named in format photoname.ext.json where ext is extension of the photo (e.g. jpg). "
+ '"exiftool -j=photoname.json photoname.jpg" '
+ "The sidecar file is named in format photoname.json "
"--sidecar xmp: create XMP sidecar used by Adobe Lightroom, etc."
- "The sidecar file is named in format photoname.ext.xmp where ext is extension of the photo (e.g. jpg). "
+ "The sidecar file is named in format photoname.xmp"
)
@click.option(
"--download-missing",
diff --git a/osxphotos/_version.py b/osxphotos/_version.py
index f229a01e..e0f4f523 100644
--- a/osxphotos/_version.py
+++ b/osxphotos/_version.py
@@ -1,3 +1,3 @@
""" version info """
-__version__ = "0.22.6"
+__version__ = "0.22.7"
diff --git a/osxphotos/photoinfo.py b/osxphotos/photoinfo.py
index 4303fd83..18a168f2 100644
--- a/osxphotos/photoinfo.py
+++ b/osxphotos/photoinfo.py
@@ -4,6 +4,7 @@ Represents a single photo in the Photos library and provides access to the photo
PhotosDB.photos() returns a list of PhotoInfo objects
"""
+import glob
import json
import logging
import os.path
@@ -32,8 +33,6 @@ from .utils import (
dd_to_dms_str,
)
-# TODO: check pylint output
-
class PhotoInfo:
"""
@@ -525,13 +524,20 @@ class PhotoInfo:
dest = dest / filename
# check to see if file exists and if so, add (1), (2), etc until we find one that works
+ # Photos checks the stem and adds (1), (2), etc which avoids collision with sidecars
+ # e.g. exporting sidecar for file1.png and file1.jpeg
+ # if file1.png exists and exporting file1.jpeg,
+ # dest will be file1 (1).jpeg even though file1.jpeg doesn't exist to prevent sidecar collision
if increment and not overwrite:
count = 1
- dest_new = dest
- while dest_new.exists():
- dest_new = dest.parent / f"{dest.stem} ({count}){dest.suffix}"
+ glob_str = str(dest.parent / f"{dest.stem}*")
+ dest_files = glob.glob(glob_str)
+ dest_files = [pathlib.Path(f).stem for f in dest_files]
+ dest_new = dest.stem
+ while dest_new in dest_files:
+ dest_new = f"{dest.stem} ({count})"
count += 1
- dest = dest_new
+ dest = dest.parent / f"{dest_new}{dest.suffix}"
# if overwrite==False and #increment==False, export should fail if file exists
if dest.exists() and not overwrite and not increment:
@@ -597,7 +603,7 @@ class PhotoInfo:
if sidecar_json:
logging.debug("writing exiftool_json_sidecar")
- sidecar_filename = f"{dest}.json"
+ sidecar_filename = dest.parent / pathlib.Path(f"{dest.stem}.json")
sidecar_str = self._exiftool_json_sidecar()
try:
self._write_sidecar(sidecar_filename, sidecar_str)
@@ -607,7 +613,7 @@ class PhotoInfo:
if sidecar_xmp:
logging.debug("writing xmp_sidecar")
- sidecar_filename = f"{dest}.xmp"
+ sidecar_filename = dest.parent / pathlib.Path(f"{dest.stem}.xmp")
sidecar_str = self._xmp_sidecar()
try:
self._write_sidecar(sidecar_filename, sidecar_str)
@@ -637,6 +643,7 @@ class PhotoInfo:
ModifyDate """
exif = {}
+ exif["_CreatedBy"] = "osxphotos, https://github.com/RhetTbull/osxphotos"
exif["FileName"] = self.filename
if self.description:
@@ -693,10 +700,14 @@ class PhotoInfo:
def _xmp_sidecar(self):
""" returns string for XMP sidecar """
+ # TODO: add additional fields to XMP file?
+
xmp_template = Template(
filename=os.path.join(_TEMPLATE_DIR, _XMP_TEMPLATE_NAME)
)
xmp_str = xmp_template.render(photo=self)
+ # remove extra lines that mako inserts from template
+ xmp_str = "\n".join([line for line in xmp_str.split("\n") if line.strip() != ""])
return xmp_str
def _write_sidecar(self, filename, sidecar_str):
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 7cf57c3c..1979c1a2 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -35,6 +35,10 @@ CLI_EXPORT_FILENAMES = [
"wedding_edited.jpg",
]
+CLI_EXPORT_UUID = "D79B8D77-BFFC-460B-9312-034F2877D35B"
+
+CLI_EXPORT_SIDECAR_FILENAMES = ["Pumkins2.jpg", "Pumpkins2.json", "Pumpkins2.xmp"]
+
def test_osxphotos():
import osxphotos
@@ -118,10 +122,36 @@ def test_query_date():
"--db",
"./tests/Test-10.15.1.photoslibrary",
"--from-date=2018-09-28",
- "--to-date=2018-09-28T23:00:00"
+ "--to-date=2018-09-28T23:00:00",
],
)
assert result.exit_code == 0
json_got = json.loads(result.output)
- assert len(json_got) == 4
\ No newline at end of file
+ assert len(json_got) == 4
+
+
+def test_export_sidecar():
+ import glob
+ import os
+ import os.path
+ import osxphotos
+ from osxphotos.__main__ import export
+
+ runner = CliRunner()
+ cwd = os.getcwd()
+ with runner.isolated_filesystem():
+ result = runner.invoke(
+ export,
+ [
+ os.path.join(cwd, "tests/Test-10.15.1.photoslibrary"),
+ ".",
+ "--original-name",
+ "-V",
+ "--sidecar json",
+ "--sidecar xmp",
+ f"--uuid {CLI_EXPORT_UUID}",
+ ],
+ )
+ files = glob.glob("*")
+ assert files.sort() == CLI_EXPORT_SIDECAR_FILENAMES.sort()
diff --git a/tests/test_export_catalina_10_15_1.py b/tests/test_export_catalina_10_15_1.py
index 5ab9ed13..f15b102d 100644
--- a/tests/test_export_catalina_10_15_1.py
+++ b/tests/test_export_catalina_10_15_1.py
@@ -56,6 +56,7 @@ UUID_DICT = {
"external_edit": "DC99FBDD-7A52-4100-A5BB-344131646C30",
"no_external_edit": "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51",
"export": "D79B8D77-BFFC-460B-9312-034F2877D35B", # "Pumkins2.jpg"
+ "xmp": "F12384F6-CD17-4151-ACBA-AE0E3688539E",
}
@@ -471,3 +472,62 @@ def test_exiftool_json_sidecar():
else:
assert item[0][1] == item[1][1]
+
+def test_xmp_sidecar():
+ import osxphotos
+
+ photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
+ photos = photosdb.photos(uuid=[UUID_DICT["xmp"]])
+
+ xmp_expected = """
+
+
+
+
+ Girls with pumpkins
+ Can we carry this?
+
+
+
+ Kids
+ Suzy
+ Katie
+
+
+ 2018-09-28T15:35:49.063000-04:00
+
+
+
+
+ Suzy
+ Katie
+
+
+
+
+
+
+ Kids
+
+
+
+
+ 2018-09-28T15:35:49
+ 2018-09-28T15:35:49
+
+
+ """
+
+ xmp_expected_lines = [line.strip() for line in xmp_expected.split("\n")]
+
+ xmp_got = photos[0]._xmp_sidecar()
+ 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
+
diff --git a/tests/test_export_mojave_10_14_6.py b/tests/test_export_mojave_10_14_6.py
index 40ffc616..69621050 100644
--- a/tests/test_export_mojave_10_14_6.py
+++ b/tests/test_export_mojave_10_14_6.py
@@ -42,6 +42,7 @@ UUID_DICT = {
"no_adjustments": "15uNd7%8RguTEgNPKHfTWw",
"export": "15uNd7%8RguTEgNPKHfTWw",
"location": "3Jn73XpSQQCluzRBMWRsMA",
+ "xmp": "8SOE9s0XQVGsuq4ONohTng",
}
@@ -413,3 +414,63 @@ def test_exiftool_json_sidecar():
assert sorted(item[0][1]) == sorted(item[1][1])
else:
assert item[0][1] == item[1][1]
+
+
+def test_xmp_sidecar():
+ import osxphotos
+
+ photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
+ photos = photosdb.photos(uuid=[UUID_DICT["xmp"]])
+
+ xmp_expected = """
+
+
+
+
+ Girls with pumpkins
+ Can we carry this?
+
+
+
+ Kids
+ Suzy
+ Katie
+
+
+ 2018-09-28T15:35:49.063000-04:00
+
+
+
+
+ Suzy
+ Katie
+
+
+
+
+
+
+ Kids
+
+
+
+
+ 2018-09-28T15:35:49
+ 2018-09-28T15:35:49
+
+
+ """
+
+ xmp_expected_lines = [line.strip() for line in xmp_expected.split("\n")]
+
+ xmp_got = photos[0]._xmp_sidecar()
+ 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
+