Skip to content

Instantly share code, notes, and snippets.

@RhetTbull
Created February 18, 2021 05:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RhetTbull/5778d8d937f3a546fc6a5ede5a5f2c3e to your computer and use it in GitHub Desktop.
Save RhetTbull/5778d8d937f3a546fc6a5ede5a5f2c3e to your computer and use it in GitHub Desktop.
Retrieve adjustment/edit data from edited photos in Apple Photos
""" Read adjustments data for photos edited in Apple's Photos.app
In Catalina and Big Sur, the adjustments data (data about edits done to the photo)
is stored in a plist file in
~/Pictures/Photos Library.photoslibrary/resources/renders/X/UUID.plist
where X is first character of the photo's UUID string and UUID is the full UUID,
e.g.: ~/Pictures/Photos Library.photoslibrary/resources/renders/3/30362C1D-192F-4CCD-9A2A-968F436DC0DE.plist
Thanks to @neilpa who figured out how to decode this information:
Reference: https://github.com/neilpa/photohack/issues/4
"""
import objc
from Foundation import NSClassFromString, NSData, NSPropertyListSerialization
# Load PhotoLibraryServices framework needed to access PFAdjustmentSerialization class
objc.loadBundle(
"PhotoLibraryServices",
bundle_path="/System/Library/PrivateFrameworks/PhotoLibraryServices.framework",
module_globals=globals(),
)
objc.registerMetaDataForSelector(
b"PFAdjustmentSerialization",
b"deserializeDictionaryFromData:error:",
{
"retval": {"type": objc._C_ID},
"arguments": {
2: {"type": objc._C_ID},
2 + 1: {"type": objc._C_PTR + objc._C_ID, "type_override": objc._C_OUT},
},
},
)
PFAdjustmentSerialization = NSClassFromString("PFAdjustmentSerialization")
def decode_adjustments_from_plist(plist_file):
with objc.autorelease_pool():
plist_data, err = NSData.dataWithContentsOfFile_options_error_(
plist_file, 0, None
)
if err:
raise ValueError(f"Error reading plist data from {plist_file}: {err}")
plist_dict = NSPropertyListSerialization.propertyListWithData_options_format_error_(
plist_data, 0, None, None
)
return PFAdjustmentSerialization.deserializeDictionaryFromData_error_(
plist_dict[0]["adjustmentData"], None
)
if __name__ == "__main__":
import sys
for plist in sys.argv[1:]:
print(plist)
adjustments = decode_adjustments_from_plist(plist)
print(adjustments)
"""
python adjustments.py "/Users/rhet/Pictures/Photos Library.photoslibrary/resources/renders/3/32906695-C79A-4509-9D68-ED96833C0BE3.plist"
/Users/rhet/Pictures/Photos Library.photoslibrary/resources/renders/3/32906695-C79A-4509-9D68-ED96833C0BE3.plist
{
adjustments = (
{
enabled = 1;
formatVersion = 1;
identifier = Effect;
settings = {
effectIntensity = 1;
effectName = 3DVividCool;
effectVersion = 1;
};
}
);
formatVersion = 1;
metadata = {
masterHeight = 4032;
masterWidth = 3024;
orientation = 1;
pipelineVersion = "OSX.4";
};
versionInfo = {
appVersion = "311.0.130";
buildNumber = 20B28;
platform = OSX;
schemaRevision = 1;
};
}
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment