Added location (latitude/longitude), closes issue #2
This commit is contained in:
@@ -342,6 +342,9 @@ Returns `True` if the picture has been marked as a favorite, otherwise `False`
|
||||
#### `hidden()`
|
||||
Returns `True` if the picture has been marked as hidden, otherwise `False`
|
||||
|
||||
#### `location()`
|
||||
Returns latitude and longitude as a tuple of floats (latitude, longitude). If location is not set, latitude and longitude are returned as `None`
|
||||
|
||||
#### `to_json()`
|
||||
Returns a JSON representation of all photo info
|
||||
|
||||
@@ -386,7 +389,7 @@ The sqlite3 database used by Photos uses write ahead logging that is updated asy
|
||||
- [Click](https://pypi.org/project/click/)
|
||||
|
||||
## Acknowledgements
|
||||
This code was inspired by photo-export by Patrick Fältström see: (https://github.com/patrikhson/photo-export) Copyright (c) 2015 Patrik Fältström paf@frobbit.se
|
||||
This project was inspired by photo-export by Patrick Fältström see: (https://github.com/patrikhson/photo-export) Copyright (c) 2015 Patrik Fältström paf@frobbit.se
|
||||
|
||||
To interact with the Photos app, I use [py-applescript]( https://github.com/rdhyee/py-applescript) by "Raymond Yee / rdhyee". Rather than import this module, I included the entire module
|
||||
(which is published as public domain code) in a private module to prevent ambiguity with
|
||||
|
||||
@@ -378,7 +378,7 @@ class PhotosDB:
|
||||
""" process the Photos database to extract info """
|
||||
""" works on Photos version <= 4.0 """
|
||||
|
||||
#TODO: Update strings to remove + (not needed)
|
||||
# TODO: Update strings to remove + (not needed)
|
||||
# Epoch is Jan 1, 2001
|
||||
td = (datetime(2001, 1, 1, 0, 0) - datetime(1970, 1, 1, 0, 0)).total_seconds()
|
||||
|
||||
@@ -491,7 +491,7 @@ class PhotosDB:
|
||||
+ "RKVersion.hasAdjustments, RKVersion.hasKeywords, RKVersion.imageTimeZoneOffsetSeconds, "
|
||||
+ "RKMaster.volumeId, RKMaster.imagePath, RKVersion.extendedDescription, RKVersion.name, "
|
||||
+ "RKMaster.isMissing, RKMaster.originalFileName, RKVersion.isFavorite, RKVersion.isHidden, "
|
||||
+ "RKVersion.longitude, RKVersion.latitude "
|
||||
+ "RKVersion.latitude, RKVersion.longitude "
|
||||
+ "from RKVersion, RKMaster where RKVersion.isInTrash = 0 and RKVersion.type = 2 and "
|
||||
+ "RKVersion.masterUuid = RKMaster.uuid and RKVersion.filename not like '%.pdf'"
|
||||
)
|
||||
@@ -529,9 +529,8 @@ class PhotosDB:
|
||||
self._dbphotos[uuid]["originalFilename"] = row[15]
|
||||
self._dbphotos[uuid]["favorite"] = row[16]
|
||||
self._dbphotos[uuid]["hidden"] = row[17]
|
||||
self._dbphotos[uuid]["longitude"] = row[18]
|
||||
self._dbphotos[uuid]["latitude"] = row[19]
|
||||
|
||||
self._dbphotos[uuid]["latitude"] = row[18]
|
||||
self._dbphotos[uuid]["longitude"] = row[19]
|
||||
|
||||
conn.close()
|
||||
|
||||
@@ -735,7 +734,9 @@ class PhotosDB:
|
||||
"ZGENERICASSET.ZHIDDEN, "
|
||||
"ZGENERICASSET.ZFAVORITE, "
|
||||
"ZGENERICASSET.ZDIRECTORY, "
|
||||
"ZGENERICASSET.ZFILENAME "
|
||||
"ZGENERICASSET.ZFILENAME, "
|
||||
"ZGENERICASSET.ZLATITUDE, "
|
||||
"ZGENERICASSET.ZLONGITUDE "
|
||||
"FROM ZGENERICASSET "
|
||||
"JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = ZGENERICASSET.Z_PK "
|
||||
"WHERE ZGENERICASSET.ZTRASHEDSTATE = 0 AND ZGENERICASSET.ZKIND = 0 "
|
||||
@@ -761,7 +762,9 @@ class PhotosDB:
|
||||
# 9 "ZGENERICASSET.ZHIDDEN, "
|
||||
# 10 "ZGENERICASSET.ZFAVORITE, "
|
||||
# 11 "ZGENERICASSET.ZDIRECTORY, "
|
||||
# 12 "ZGENERICASSET.ZFILENAME "
|
||||
# 12 "ZGENERICASSET.ZFILENAME, "
|
||||
# 13 "ZGENERICASSET.ZLATITUDE, "
|
||||
# 14 "ZGENERICASSET.ZLONGITUDE "
|
||||
|
||||
i = 0
|
||||
for row in c:
|
||||
@@ -799,6 +802,15 @@ class PhotosDB:
|
||||
self._dbphotos[uuid]["filename"] = row[12]
|
||||
self._dbphotos[uuid]["directory"] = row[11]
|
||||
|
||||
# set latitude and longitude
|
||||
# if both latitude and longitude = -180.0, then they are NULL
|
||||
if row[13] == -180.0 and row[14] == -180.0:
|
||||
self._dbphotos[uuid]["latitude"] = None
|
||||
self._dbphotos[uuid]["longitude"] = None
|
||||
else:
|
||||
self._dbphotos[uuid]["latitude"] = row[13]
|
||||
self._dbphotos[uuid]["longitude"] = row[14]
|
||||
|
||||
# these will get filled in later
|
||||
# init to avoid key errors
|
||||
self._dbphotos[uuid]["extendedDescription"] = None # fill this in later
|
||||
@@ -1087,14 +1099,18 @@ class PhotoInfo:
|
||||
""" True if picture is hidden """
|
||||
return True if self.__info["hidden"] == 1 else False
|
||||
|
||||
def longitude(self):
|
||||
def location(self):
|
||||
""" returns (latitude, longitude) as float in degrees or None """
|
||||
return (self._latitude(), self._longitude())
|
||||
|
||||
def _longitude(self):
|
||||
""" Returns longitude, in degrees """
|
||||
return self.__info["longitude"]
|
||||
|
||||
def latitude(self):
|
||||
def _latitude(self):
|
||||
""" Returns latitude, in degrees """
|
||||
return self.__info["latitude"]
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f"osxphotos.PhotoInfo(db={self.__db}, uuid='{self.__uuid}', info={self.__info})"
|
||||
|
||||
|
||||
2
setup.py
2
setup.py
@@ -38,7 +38,7 @@ with open(path.join(this_directory, "README.md"), encoding="utf-8") as f:
|
||||
|
||||
setup(
|
||||
name="osxphotos",
|
||||
version="0.14.4",
|
||||
version="0.14.5",
|
||||
description="Manipulate (read-only) Apple's Photos app library on Mac OS X",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
|
||||
@@ -181,6 +181,30 @@ def test_not_hidden():
|
||||
p = photos[0]
|
||||
assert p.hidden() == False
|
||||
|
||||
def test_location_1():
|
||||
# test photo with lat/lon info
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["DC99FBDD-7A52-4100-A5BB-344131646C30"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
lat, lon = p.location()
|
||||
assert lat == pytest.approx(51.50357167)
|
||||
assert lon == pytest.approx(-0.1318055)
|
||||
|
||||
|
||||
def test_location_2():
|
||||
# test photo with no location info
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["6191423D-8DB8-4D4C-92BE-9BBBA308AAC4"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
lat, lon = p.location()
|
||||
assert lat is None
|
||||
assert lon is None
|
||||
|
||||
def test_count():
|
||||
import osxphotos
|
||||
|
||||
@@ -181,6 +181,30 @@ def test_not_hidden():
|
||||
assert p.hidden() == False
|
||||
|
||||
|
||||
def test_location_1():
|
||||
#test photo with lat/lon info
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["3Jn73XpSQQCluzRBMWRsMA"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
lat, lon = p.location()
|
||||
assert lat == pytest.approx(51.50357167)
|
||||
assert lon == pytest.approx(-0.1318055)
|
||||
|
||||
def test_location_2():
|
||||
# test photo with no location info
|
||||
import osxphotos
|
||||
|
||||
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB)
|
||||
photos = photosdb.photos(uuid=["YZFCPY24TUySvpu7owiqxA"])
|
||||
assert len(photos) == 1
|
||||
p = photos[0]
|
||||
lat, lon = p.location()
|
||||
assert lat is None
|
||||
assert lon is None
|
||||
|
||||
def test_count():
|
||||
import osxphotos
|
||||
|
||||
|
||||
Reference in New Issue
Block a user