diff --git a/README.md b/README.md
index 80e3e195..6f9f7446 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@
- [`hidden()`](#hidden)
- [`location()`](#location)
- [`to_json()`](#to_json)
- - [`export(*args, edited=False, overwrite=False, increment=True)`](#exportargs-editedfalse-overwritefalse-incrementtrue)
+ - [`export(dest, *filename, edited=False, overwrite=False, increment=True)`](#exportdest-filename-editedfalse-overwritefalse-incrementtrue)
+ [Examples](#examples)
* [History](#history)
* [Contributing](#contributing)
@@ -490,10 +490,10 @@ Returns latitude and longitude as a tuple of floats (latitude, longitude). If l
#### `to_json()`
Returns a JSON representation of all photo info
-#### `export(*args, edited=False, overwrite=False, increment=True)`
+#### `export(dest, *filename, edited=False, overwrite=False, increment=True)`
Export photo from the Photos library to another destination on disk.
-- First argument of *args must be valid destination path (or exception raised).
-- Second argument of *args (optional): name of picture; if not provided, will use current filename
+- First argument dest must be valid destination path (or exception raised).
+- Second argument *filename (optional): name of picture; if not provided, will use current filename
- edited: boolean; if True (default=False), will export the edited version of the photo (or raise exception if no edited version)
- overwrite: boolean; if True (default=False), will overwrite files if they alreay exist
- increment: boolean; if True (default=True), will increment file name until a non-existant name is found
diff --git a/osxphotos/__init__.py b/osxphotos/__init__.py
index 79f4d7cc..2361914b 100644
--- a/osxphotos/__init__.py
+++ b/osxphotos/__init__.py
@@ -4,6 +4,7 @@ import logging
import os.path
import pathlib
import platform
+import re
import sqlite3
import subprocess
import sys
@@ -88,8 +89,8 @@ def _get_os_version():
def _check_file_exists(filename):
- # returns true if file exists and is not a directory
- # otherwise returns false
+ """ returns true if file exists and is not a directory
+ otherwise returns false """
filename = os.path.abspath(filename)
return os.path.exists(filename) and not os.path.isdir(filename)
@@ -112,6 +113,54 @@ def _get_resource_loc(model_id):
return folder_id, file_id
+def _dd_to_dms(dd):
+ """ convert lat or lon in decimal degrees (dd) to degrees, minutes, seconds """
+ """ return tuple of int(deg), int(min), float(sec) """
+ dd = float(dd)
+ negative = dd < 0
+ dd = abs(dd)
+ min_, sec_ = divmod(dd * 3600, 60)
+ deg_, min_ = divmod(min_, 60)
+ if negative:
+ if deg_ > 0:
+ deg_ = deg_ * -1
+ elif min_ > 0:
+ min_ = min_ * -1
+ else:
+ sec_ = sec_ * -1
+
+ return int(deg_), int(min_), sec_
+
+
+def dd_to_dms_str(lat, lon):
+ """ convert latitude, longitude in degrees to degrees, minutes, seconds as string """
+ """ lat: latitude in degrees """
+ """ lon: longitude in degrees """
+ """ returns: string tuple in format ("51 deg 30' 12.86\" N", "0 deg 7' 54.50\" W") """
+ """ this is the same format used by exiftool's json format """
+ # TODO: add this to readme
+
+ lat_deg, lat_min, lat_sec = _dd_to_dms(lat)
+ lon_deg, lon_min, lon_sec = _dd_to_dms(lon)
+
+ lat_hemisphere = "N"
+ if any([lat_deg < 0, lat_min < 0, lat_sec < 0]):
+ lat_hemisphere = "S"
+
+ lon_hemisphere = "E"
+ if any([lon_deg < 0, lon_min < 0, lon_sec < 0]):
+ lon_hemisphere = "W"
+
+ lat_str = (
+ f"{abs(lat_deg)} deg {abs(lat_min)}' {abs(lat_sec):.2f}\" {lat_hemisphere}"
+ )
+ lon_str = (
+ f"{abs(lon_deg)} deg {abs(lon_min)}' {abs(lon_sec):.2f}\" {lon_hemisphere}"
+ )
+
+ return lat_str, lon_str
+
+
def get_system_library_path():
""" return the path to the system Photos library as string """
""" only works on MacOS 10.15+ """
@@ -1464,7 +1513,7 @@ class PhotoInfo:
""" returns (latitude, longitude) as float in degrees or None """
return (self._latitude(), self._longitude())
- def export(self, *args, edited=False, overwrite=False, increment=True):
+ def export(self, dest, *filename, edited=False, overwrite=False, increment=True):
""" export photo """
""" first argument must be valid destination path (or exception raised) """
""" second argument (optional): name of picture; if not provided, will use current filename """
@@ -1478,44 +1527,37 @@ class PhotoInfo:
# maybe dest, *filename?
# check arguments and get destination path and filename (if provided)
- dest = None # destination path
- filename = None # photo filename
- if not args:
- # need at least one arg (destination)
- raise TypeError("Must pass destination as first argument")
+ if filename and len(filename) > 2:
+ raise TypeError(
+ "Too many positional arguments. Should be at most two: destination, filename."
+ )
else:
- if len(args) > 2:
- raise TypeError(
- "Too many positional arguments. Should be at most two: destination, filename."
- )
- else:
- # verify destination is a valid path
- dest = args[0]
- if dest is None:
- raise ValueError("Destination must not be None")
- elif not os.path.isdir(dest):
- raise FileNotFoundError("Invalid path passed to export")
+ # verify destination is a valid path
+ if dest is None:
+ raise ValueError("Destination must not be None")
+ elif not os.path.isdir(dest):
+ raise FileNotFoundError("Invalid path passed to export")
- if len(args) == 2:
- # second arg is filename of picture
- filename = args[1]
- else:
- # no filename provided so use the default
- # if edited file requested, use filename but add _edited
- # need to use file extension from edited file as Photos saves a jpeg once edited
- if edited:
- # verify we have a valid path_edited and use that to get filename
- if not self.path_edited():
- raise FileNotFoundError(
- f"edited=True but path_edited is none; hasadjustments: {self.hasadjustments()}"
- )
- edited_name = Path(self.path_edited()).name
- edited_suffix = Path(edited_name).suffix
- filename = (
- Path(self.filename()).stem + "_edited" + edited_suffix
+ if filename and len(filename) == 1:
+ # second arg is filename of picture
+ filename = filename[0]
+ else:
+ # no filename provided so use the default
+ # if edited file requested, use filename but add _edited
+ # need to use file extension from edited file as Photos saves a jpeg once edited
+ if edited:
+ # verify we have a valid path_edited and use that to get filename
+ if not self.path_edited():
+ raise FileNotFoundError(
+ f"edited=True but path_edited is none; hasadjustments: {self.hasadjustments()}"
)
- else:
- filename = self.filename()
+ edited_name = Path(self.path_edited()).name
+ edited_suffix = Path(edited_name).suffix
+ filename = (
+ Path(self.filename()).stem + "_edited" + edited_suffix
+ )
+ else:
+ filename = self.filename()
# 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?
@@ -1584,6 +1626,66 @@ class PhotoInfo:
return str(dest)
+ def _exiftool_json_sidecar(self):
+ """ return json string of EXIF details in exiftool sidecar format """
+ exif = {}
+ exif["FileName"] = self.filename()
+
+ if self.description():
+ exif["ImageDescription"] = self.description()
+ exif["Description"] = self.description()
+
+ if self.title():
+ exif["Title"] = self.title()
+
+ if self.keywords():
+ exif["TagsList"] = exif["Keywords"] = self.keywords()
+
+ if self.persons():
+ exif["PersonInImage"] = self.persons()
+
+ # if self.favorite():
+ # exif["Rating"] = 5
+
+ (lat, lon) = self.location()
+ if lat is not None and lon is not None:
+ lat_str, lon_str = dd_to_dms_str(lat, lon)
+ exif["GPSLatitude"] = lat_str
+ exif["GPSLongitude"] = lon_str
+ exif["GPSPosition"] = f"{lat_str}, {lon_str}"
+ lat_ref = "North" if lat >= 0 else "South"
+ lon_ref = "East" if lon >= 0 else "West"
+ exif["GPSLatitudeRef"] = lat_ref
+ exif["GPSLongitudeRef"] = lon_ref
+
+ # process date/time and timezone offset
+ date = self.date()
+ # exiftool expects format to "2015:01:18 12:00:00"
+ datetimeoriginal = date.strftime("%Y:%m:%d %H:%M:%S")
+ offsettime = date.strftime("%z")
+ # find timezone offset in format "-04:00"
+ offset = re.findall(r"([+-]?)([\d]{2})([\d]{2})", offsettime)
+ offset = offset[0] # findall returns list of tuples
+ offsettime = f"{offset[0]}{offset[1]}:{offset[2]}"
+ exif["DateTimeOriginal"] = datetimeoriginal
+ exif["OffsetTimeOriginal"] = offsettime
+
+ json_str = json.dumps([exif])
+ return json_str
+
+ def _write_sidecar_car(self, filename, json_str):
+ if not filename and not json_str:
+ raise (
+ ValueError(
+ f"filename {filename} and json_str {json_str} must not be None"
+ )
+ )
+
+ # TODO: catch exception?
+ f = open(filename, "w")
+ f.write(json_str)
+ f.close()
+
def _longitude(self):
""" Returns longitude, in degrees """
return self._info["longitude"]
diff --git a/tests/Test-10.12.6.photoslibrary/database/photos.db b/tests/Test-10.12.6.photoslibrary/database/photos.db
index c3b177f5..63f748fd 100644
Binary files a/tests/Test-10.12.6.photoslibrary/database/photos.db and b/tests/Test-10.12.6.photoslibrary/database/photos.db differ
diff --git a/tests/Test-10.12.6.photoslibrary/private/com.apple.Photos/appPrivateData.plist b/tests/Test-10.12.6.photoslibrary/private/com.apple.Photos/appPrivateData.plist
index f8d962c6..8f574f91 100644
--- a/tests/Test-10.12.6.photoslibrary/private/com.apple.Photos/appPrivateData.plist
+++ b/tests/Test-10.12.6.photoslibrary/private/com.apple.Photos/appPrivateData.plist
@@ -5,7 +5,7 @@
LithiumMessageTracer
LastReportedDate
- 2019-08-24T02:50:48Z
+ 2019-12-08T16:44:38Z
PXPeopleScreenUnlocked
diff --git a/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotoAnalysisServicePreferences.plist b/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotoAnalysisServicePreferences.plist
index 9f33ea62..64a53907 100644
--- a/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotoAnalysisServicePreferences.plist
+++ b/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotoAnalysisServicePreferences.plist
@@ -3,8 +3,8 @@
PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate
- 2019-12-07T16:40:40Z
+ 2019-12-16T02:55:50Z
PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate
- 2019-12-07T16:40:41Z
+ 2019-12-16T02:55:50Z
diff --git a/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph-tmp.graphdb-shm b/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph-tmp.graphdb-shm
index ca5a3e01..fe9ac284 100644
Binary files a/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph-tmp.graphdb-shm and b/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph-tmp.graphdb-shm differ
diff --git a/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph.graphdb b/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph.graphdb
index de2fb528..8bc5d0c5 100644
Binary files a/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph.graphdb and b/tests/Test-10.12.6.photoslibrary/private/com.apple.photoanalysisd/GraphService/PhotosGraph/photosgraph.graphdb differ
diff --git a/tests/Test-10.12.6.photoslibrary/resources/moments/analysismetadata.plist b/tests/Test-10.12.6.photoslibrary/resources/moments/analysismetadata.plist
index af372a50..e50e7b20 100644
--- a/tests/Test-10.12.6.photoslibrary/resources/moments/analysismetadata.plist
+++ b/tests/Test-10.12.6.photoslibrary/resources/moments/analysismetadata.plist
@@ -11,6 +11,6 @@
PLLastRevGeoForcedProviderOutOfDateCheckVersionKey
1
PLLastRevGeoVerFileFetchDateKey
- 2019-12-07T16:40:32Z
+ 2019-12-13T18:43:07Z
diff --git a/tests/Test-10.12.6.photoslibrary/resources/moments/needsanalysis b/tests/Test-10.12.6.photoslibrary/resources/moments/needsanalysis
deleted file mode 100644
index e69de29b..00000000
diff --git a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite
index 6d96e0ee..76934303 100644
Binary files a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite and b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite differ
diff --git a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-shm b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-shm
index 6eaaf26a..575a5b55 100644
Binary files a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal
index d1a5720f..c30ab565 100644
Binary files a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite.lock b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite.lock
index a1922ade..c714c196 100644
--- a/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite.lock
+++ b/tests/Test-10.15.1.photoslibrary/database/Photos.sqlite.lock
@@ -7,7 +7,7 @@
hostuuid
9575E48B-8D5F-5654-ABAC-4431B1167324
pid
- 423
+ 1794
processname
photolibraryd
uid
diff --git a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite
index 7ac8d691..caab56b4 100644
Binary files a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite and b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite differ
diff --git a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm
index c56e485b..794cd7aa 100644
Binary files a/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/database/search/psi.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db
index 90d9b9c0..660e2c9c 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db and b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm
index d19297a4..dc74fbd0 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.mediaanalysisd/MediaAnalysis/mediaanalysis.db-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm
index 16e828ae..0f49ab6f 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal
index 911b1dcd..9725d18c 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.AOI.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm
index d9e13d96..135fc83e 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal
index 3868a639..5ede4432 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.Nature.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm
index 75fb129f..8e9ca931 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal
index 46632fa6..4cd2c6d1 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.POI.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm
index ef176a0e..4736b98c 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal
index 5cb86691..f900bb9a 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSBusinessCategoryCache.ROI.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite
index f2aa6a77..3e8c455f 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm
index 15b02dc9..311ddda9 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-wal
index 0c48f097..639e0875 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSContactCache.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm
index a3fdd593..5c90dc48 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSLocationCache.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm
index ffc30a49..871cd4a0 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-wal
index df9bc056..f85bc76b 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/CLSPublicEventCache.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm
index 822c040d..290c5025 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-shm differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal
index a8613bd7..f182634f 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGCurationCache.sqlite.sqlite-wal differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGSearchComputationCache.plist b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGSearchComputationCache.plist
index 05b10a46..5a74827a 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGSearchComputationCache.plist and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PGSearchComputationCache.plist differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist
index 5bbae0b4..221843bf 100644
--- a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist
+++ b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotoAnalysisServicePreferences.plist
@@ -3,24 +3,24 @@
BackgroundHighlightCollection
- 2019-12-14T18:19:30Z
+ 2019-12-15T18:49:56Z
BackgroundHighlightEnrichment
- 2019-12-14T18:19:29Z
+ 2019-12-15T18:49:35Z
BackgroundJobAssetRevGeocode
- 2019-12-14T18:19:30Z
+ 2019-12-15T20:55:19Z
BackgroundJobSearch
- 2019-12-14T18:19:30Z
+ 2019-12-15T18:49:56Z
BackgroundPeopleSuggestion
- 2019-12-14T18:19:28Z
+ 2019-12-15T18:49:35Z
BackgroundUserBehaviorProcessor
- 0000-12-30T00:00:00Z
+ 2019-12-15T18:49:56Z
PhotoAnalysisGraphLastBackgroundGraphConsistencyUpdateJobDateKey
- 2019-12-14T18:19:28Z
+ 2019-12-15T20:55:19Z
PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate
- 2019-12-14T18:19:28Z
+ 2019-12-15T18:49:35Z
PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate
- 2019-12-10T06:45:58Z
+ 2019-12-15T20:55:19Z
SiriPortraitDonation
- 0000-12-30T00:00:00Z
+ 2019-12-15T18:49:56Z
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb
index 15c03a40..e6afe24e 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/graph/PhotosGraph/photosgraph.kgdb differ
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/PhotoAnalysisServicePreferences.plist b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/PhotoAnalysisServicePreferences.plist
index fb706463..19f59c1c 100644
--- a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/PhotoAnalysisServicePreferences.plist
+++ b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/PhotoAnalysisServicePreferences.plist
@@ -3,8 +3,8 @@
FaceIDModelLastGenerationKey
- 2019-12-10T06:45:58Z
+ 2019-12-15T18:49:56Z
LastContactClassificationKey
- 2019-12-10T06:46:00Z
+ 2019-12-15T18:49:58Z
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/faceWorkerState.plist b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/faceWorkerState.plist
index c36206de..736db69d 100644
--- a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/faceWorkerState.plist
+++ b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/faceWorkerState.plist
@@ -3,7 +3,7 @@
IncrementalPersonProcessingStage
- 6
+ 0
PersonBuilderLastMinimumFaceGroupSizeForCreatingMergeCandidates
15
PersonBuilderMergeCandidatesEnabled
diff --git a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/vnpersonsmodel.bin b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/vnpersonsmodel.bin
index 34dcccbd..8a59c844 100644
Binary files a/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/vnpersonsmodel.bin and b/tests/Test-10.15.1.photoslibrary/private/com.apple.photoanalysisd/caches/vision/vnpersonsmodel.bin differ
diff --git a/tests/test_export_catalina_10_15_1.py b/tests/test_export_catalina_10_15_1.py
index a3ce57ac..df9159f6 100644
--- a/tests/test_export_catalina_10_15_1.py
+++ b/tests/test_export_catalina_10_15_1.py
@@ -57,6 +57,7 @@ UUID_DICT = {
"export": "D79B8D77-BFFC-460B-9312-034F2877D35B", # "Pumkins2.jpg"
}
+
def test_export_1():
# test basic export
# get an unedited image and export it using default filename
@@ -390,3 +391,75 @@ def test_export_13():
with pytest.raises(Exception) as e:
assert photos[0].export(dest)
assert e.type == type(FileNotFoundError())
+
+
+def test_dd_to_dms_str_1():
+ import osxphotos
+
+ lat_str, lon_str = osxphotos.dd_to_dms_str(
+ 34.559331096, 69.206499174
+ ) # Kabul, 34°33'33.59" N 69°12'23.40" E
+
+ assert lat_str == "34 deg 33' 33.59\" N"
+ assert lon_str == "69 deg 12' 23.40\" E"
+
+
+def test_dd_to_dms_str_2():
+ import osxphotos
+
+ lat_str, lon_str = osxphotos.dd_to_dms_str(
+ -34.601997592, -58.375665164
+ ) # Buenos Aires, 34°36'7.19" S 58°22'32.39" W
+
+ assert lat_str == "34 deg 36' 7.19\" S"
+ assert lon_str == "58 deg 22' 32.39\" W"
+
+
+def test_dd_to_dms_str_3():
+ import osxphotos
+
+ lat_str, lon_str = osxphotos.dd_to_dms_str(
+ -1.2666656, 36.7999968
+ ) # Nairobi, 1°15'60.00" S 36°47'59.99" E
+
+ assert lat_str == "1 deg 15' 60.00\" S"
+ assert lon_str == "36 deg 47' 59.99\" E"
+
+
+def test_dd_to_dms_str_4():
+ import osxphotos
+
+ lat_str, lon_str = osxphotos.dd_to_dms_str(
+ 38.889248, -77.050636
+ ) # DC: 38° 53' 21.2928" N, 77° 3' 2.2896" W
+
+ assert lat_str == "38 deg 53' 21.29\" N"
+ assert lon_str == "77 deg 3' 2.29\" W"
+
+
+def test_exiftool_json_sidecar():
+ import osxphotos
+ import json
+
+ photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
+ photos = photosdb.photos(uuid=[UUID_DICT["location"]])
+
+ json_expected = json.loads(
+ """
+ [{"FileName": "DC99FBDD-7A52-4100-A5BB-344131646C30.jpeg",
+ "Title": "St. James\'s Park",
+ "TagsList": ["London 2018", "St. James\'s Park", "England", "United Kingdom", "UK", "London"],
+ "Keywords": ["London 2018", "St. James\'s Park", "England", "United Kingdom", "UK", "London"],
+ "GPSLatitude": "51 deg 30\' 12.86\\" N",
+ "GPSLongitude": "0 deg 7\' 54.50\\" W",
+ "GPSPosition": "51 deg 30\' 12.86\\" N, 0 deg 7\' 54.50\\" W",
+ "GPSLatitudeRef": "North", "GPSLongitudeRef": "West",
+ "DateTimeOriginal": "2018:10:13 09:18:12", "OffsetTimeOriginal": "-04:00"}]
+ """
+ )
+
+ json_got = photos[0]._exiftool_json_sidecar()
+ json_got = json.loads(json_got)
+
+ assert sorted(json_got[0].items()) == sorted(json_expected[0].items())
+