Initial release for MacOS 10.15 / Photos 5
This commit is contained in:
@@ -11,9 +11,9 @@ OSXPhotos provides the ability to interact with and query Apple's Photos app lib
|
|||||||
|
|
||||||
Only works on Mac OS X. Tested on Mac OS 10.12.6 / Photos 2.0, 10.13.6 / Photos 3.0 and Mac OS 10.14.5, 10.14.6 / Photos 4.0. Requires python >= 3.6
|
Only works on Mac OS X. Tested on Mac OS 10.12.6 / Photos 2.0, 10.13.6 / Photos 3.0 and Mac OS 10.14.5, 10.14.6 / Photos 4.0. Requires python >= 3.6
|
||||||
|
|
||||||
This module will read Photos databases for any supported version on any supported OS version. E.g. you can read a database created with Photos 4.0 on Mac OS 10.14 on a machine running Mac OS 10.12
|
NOTE: Alpha support for Mac OS 10.15.0 / Photos 5.0. Photos 5.0 uses a new database format which required rewrite of much of the code for this package. If you find bugs, please open an issue on the project [github](https://github.com/RhetTbull/osxphotos) page.
|
||||||
|
|
||||||
NOTE: Does NOT currently support Mac OS 10.15 / Catalina and Photos 5.0. This module will run on Catalina but you will only be able to read Photos database files created with Photos 4.0 or lower. Photos 5.0 on Catalina uses an entirely new database schema and the code has not yet been udpated to work with this version.
|
This module will read Photos databases for any supported version on any supported OS version. E.g. you can read a database created with Photos 4.0 on Mac OS 10.14 on a machine running Mac OS 10.12
|
||||||
|
|
||||||
|
|
||||||
## Installation instructions
|
## Installation instructions
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ from . import _applescript
|
|||||||
|
|
||||||
# from loguru import logger
|
# from loguru import logger
|
||||||
|
|
||||||
|
# TODO: Add test for 10.15 / Photos 5
|
||||||
|
# TODO: Update README.md for Photos 5 changes
|
||||||
# TODO: Add favorites, hidden
|
# TODO: Add favorites, hidden
|
||||||
# TODO: Add location
|
# TODO: Add location
|
||||||
# TODO: Does not work with Photos 5.0 / Mac OS 10.15 Catalina
|
|
||||||
# TODO: standardize _ and __ as leading char for private variables
|
# TODO: standardize _ and __ as leading char for private variables
|
||||||
# TODO: fix docstrings
|
# TODO: fix docstrings
|
||||||
|
# TODO: handle Null person for Photos 5
|
||||||
|
|
||||||
# which Photos library database versions have been tested
|
# which Photos library database versions have been tested
|
||||||
# Photos 2.0 (10.12.6) == 2622
|
# Photos 2.0 (10.12.6) == 2622
|
||||||
@@ -35,16 +37,16 @@ from . import _applescript
|
|||||||
# Photos 4.0 (10.14.6) == 4025
|
# Photos 4.0 (10.14.6) == 4025
|
||||||
# Photos 5.0 (10.15.0) == 6000
|
# Photos 5.0 (10.15.0) == 6000
|
||||||
# TODO: Should this also use compatibleBackToVersion from LiGlobals?
|
# TODO: Should this also use compatibleBackToVersion from LiGlobals?
|
||||||
_TESTED_DB_VERSIONS = ["4025", "4016", "3301", "2622"]
|
_TESTED_DB_VERSIONS = ["6000", "4025", "4016", "3301", "2622"]
|
||||||
|
|
||||||
# versions later than this have a different database structure
|
# versions later than this have a different database structure
|
||||||
_PHOTOS_5_VERSION = "6000"
|
_PHOTOS_5_VERSION = "6000"
|
||||||
|
|
||||||
# which major version operating systems have been tested
|
# which major version operating systems have been tested
|
||||||
_TESTED_OS_VERSIONS = ["12", "13", "14"]
|
_TESTED_OS_VERSIONS = ["12", "13", "14", "15"]
|
||||||
|
|
||||||
# set _debug = True to enable debug output
|
# set _debug = True to enable debug output
|
||||||
_debug = True
|
_debug = False
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.DEBUG,
|
level=logging.DEBUG,
|
||||||
format="%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(message)s",
|
format="%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(message)s",
|
||||||
@@ -536,9 +538,9 @@ class PhotosDB:
|
|||||||
self._dbphotos[uuid]["modelID"] = row[1]
|
self._dbphotos[uuid]["modelID"] = row[1]
|
||||||
self._dbphotos[uuid]["masterUuid"] = row[2]
|
self._dbphotos[uuid]["masterUuid"] = row[2]
|
||||||
self._dbphotos[uuid]["filename"] = row[3]
|
self._dbphotos[uuid]["filename"] = row[3]
|
||||||
|
|
||||||
# TODO: Photos 5 has filename and originalFilename, is there equiv for older database formats?
|
# TODO: Photos 5 has filename and originalFilename, is there equiv for older database formats?
|
||||||
self._dbphotos[uuid]["originalFilename"] = row[3]
|
self._dbphotos[uuid]["originalFilename"] = row[3]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._dbphotos[uuid]["lastmodifieddate"] = datetime.fromtimestamp(
|
self._dbphotos[uuid]["lastmodifieddate"] = datetime.fromtimestamp(
|
||||||
@@ -1079,11 +1081,11 @@ class PhotoInfo:
|
|||||||
self.__db = db
|
self.__db = db
|
||||||
|
|
||||||
def filename(self):
|
def filename(self):
|
||||||
""" return filename of the picture """
|
""" filename of the picture """
|
||||||
return self.__info["filename"]
|
return self.__info["filename"]
|
||||||
|
|
||||||
def original_filename(self):
|
def original_filename(self):
|
||||||
""" return original filename of the picture """
|
""" original filename of the picture """
|
||||||
""" Photos 5 mangles filenames upon import """
|
""" Photos 5 mangles filenames upon import """
|
||||||
return self.__info["originalFilename"]
|
return self.__info["originalFilename"]
|
||||||
|
|
||||||
@@ -1100,6 +1102,7 @@ class PhotoInfo:
|
|||||||
return self.__info["imageTimeZoneOffsetSeconds"]
|
return self.__info["imageTimeZoneOffsetSeconds"]
|
||||||
|
|
||||||
def path(self):
|
def path(self):
|
||||||
|
""" path on disk of the picture """
|
||||||
photopath = ""
|
photopath = ""
|
||||||
|
|
||||||
if self.__db._db_version < _PHOTOS_5_VERSION:
|
if self.__db._db_version < _PHOTOS_5_VERSION:
|
||||||
@@ -1132,30 +1135,39 @@ class PhotoInfo:
|
|||||||
self.__info["filename"],
|
self.__info["filename"],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logging.debug("WARNING: masterFingerprint null",pformat(self.__info))
|
photopath = None
|
||||||
|
logging.debug("WARNING: masterFingerprint null", pformat(self.__info))
|
||||||
|
|
||||||
|
# TODO: fix the logic for isMissing
|
||||||
|
if self.__info["isMissing"] == 1:
|
||||||
|
photopath = None # path would be meaningless until downloaded
|
||||||
|
|
||||||
logging.debug(photopath)
|
logging.debug(photopath)
|
||||||
return photopath
|
|
||||||
# ZZZ
|
|
||||||
|
|
||||||
return photopath
|
return photopath
|
||||||
|
|
||||||
def description(self):
|
def description(self):
|
||||||
|
""" long / extended description of picture """
|
||||||
return self.__info["extendedDescription"]
|
return self.__info["extendedDescription"]
|
||||||
|
|
||||||
def persons(self):
|
def persons(self):
|
||||||
|
""" list of persons in picture """
|
||||||
return self.__info["persons"]
|
return self.__info["persons"]
|
||||||
|
|
||||||
def albums(self):
|
def albums(self):
|
||||||
|
""" list of albums picture is contained in """
|
||||||
return self.__info["albums"]
|
return self.__info["albums"]
|
||||||
|
|
||||||
def keywords(self):
|
def keywords(self):
|
||||||
|
""" list of keywords for picture """
|
||||||
return self.__info["keywords"]
|
return self.__info["keywords"]
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
|
""" name / title of picture """
|
||||||
return self.__info["name"]
|
return self.__info["name"]
|
||||||
|
|
||||||
def uuid(self):
|
def uuid(self):
|
||||||
|
""" UUID of picture """
|
||||||
return self.__uuid
|
return self.__uuid
|
||||||
|
|
||||||
def ismissing(self):
|
def ismissing(self):
|
||||||
@@ -1171,6 +1183,8 @@ class PhotoInfo:
|
|||||||
return True if self.__info["isMissing"] == 1 else False
|
return True if self.__info["isMissing"] == 1 else False
|
||||||
|
|
||||||
def hasadjustments(self):
|
def hasadjustments(self):
|
||||||
|
""" True if picture has adjustments """
|
||||||
|
""" TODO: not accurate for Photos version >= 5 """
|
||||||
return True if self.__info["hasAdjustments"] == 1 else False
|
return True if self.__info["hasAdjustments"] == 1 else False
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -1180,6 +1194,7 @@ class PhotoInfo:
|
|||||||
info = {
|
info = {
|
||||||
"uuid": self.uuid(),
|
"uuid": self.uuid(),
|
||||||
"filename": self.filename(),
|
"filename": self.filename(),
|
||||||
|
"original_filename": self.original_filename(),
|
||||||
"date": str(self.date()),
|
"date": str(self.date()),
|
||||||
"description": self.description(),
|
"description": self.description(),
|
||||||
"name": self.name(),
|
"name": self.name(),
|
||||||
@@ -1194,7 +1209,7 @@ class PhotoInfo:
|
|||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
""" return JSON representation """
|
""" return JSON representation """
|
||||||
#TODO: Add additional details here
|
# TODO: Add additional details here
|
||||||
pic = {
|
pic = {
|
||||||
"uuid": self.uuid(),
|
"uuid": self.uuid(),
|
||||||
"filename": self.filename(),
|
"filename": self.filename(),
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -38,7 +38,7 @@ with open(path.join(this_directory, "README.md"), encoding="utf-8") as f:
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="osxphotos",
|
name="osxphotos",
|
||||||
version="0.12.5",
|
version="0.13.0",
|
||||||
description="Manipulate (read-only) Apple's Photos app library on Mac OS X",
|
description="Manipulate (read-only) Apple's Photos app library on Mac OS X",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
|
|||||||
Reference in New Issue
Block a user