-
Notifications
You must be signed in to change notification settings - Fork 104
Structure of the code
Rhet Turnbull edited this page Jan 2, 2022
·
22 revisions
osxphotos is built around a set of classes that each handle a particular aspect of the Photos library:
- PhotosDB: reads the Photos library database and extract all relevant metadata about each photo. This is starting point for any use of osxphotos.
- PhotoInfo: represents an individual photo in the library and all associated metadata.
- AlbumInfo: represents an album in the library.
- FolderInfo: represents a folder in the library.
- PlaceInfo: represents the reverse geolocation info (e.g. the address or place name) associated with a photo.
- ExifTool: provides an interface to the exiftool tool for manipulating EXIF information associated with a photo.
- PhotoTemplate: helper class for rendering a template string.
The package code is organized in osxphotos per the table below. Most of the files contain doc strings describing what they contain.
file | description |
---|---|
__init__.py |
this is what gets read by "import osxphotos" |
__main__.py |
the command line interface, what gets executed with python3 -m osxphotos |
_constants.py |
constants used by the other modules |
_version.py |
version string |
albuminfo.py |
AlbumInfo and FolderInfo classes |
configoptions.py |
ConfigOptions class to load/save config settings for osxphotos CLI |
datetime_formatter.py |
Utility functions for formatting datetime objects, used by phototemplate.py |
datetime_utils.py |
Utility functions for working with datetime objects |
exiftool.py |
ExifTool class |
export_db.py |
ExportDB class used by CLI to cache data about exports |
fileutil.py |
FileUtil class used by CLI and export for enabling --dry-run option |
imageconverter.py |
ImageConverter class that converts an image to JPEG using CoreImage |
pathutils.py |
Utility functions for validating/sanitizing path components |
photokit.py |
Interface to Apple's PhotoKit framework for direct access to photos stored in the user's Photos library. |
photoinfo.py |
PhotoInfo class |
photosdb/ |
PhotosDB class |
phototemplate.py |
PhotoTemplate class |
placeinfo.py |
PlaceInfo class |
templates |
contains mako templates for sidecar export (though the name is similar, these templates are not associated with phototemplate.py
|
templates/xmp_sidecar.mako |
mako template used to create XMP sidecar files |
utils.py |
various utility methods--this file is getting bloated and needs to be refactored |
Broadly, osxphotos works like this:
- Opens a readonly copy of
photos.db
. If the file is locked, a temporary copy will be created then opened. - Reads
photos.db
to determine which version of Photos created the library (PhotosDB._get_db_version()
) - If library created with Photos version 2, 3, or 4,
PhotosDB._process_database4()
processes the database file to extract all required information. - If library created with Photos 5, the actual database is
Photos.sqlite
so this is opened readonly or copied to a temporary file if locked, thenPhotosDB._process_database5()
is called to process the database file and extra all required information. -
_process_database4
and_process_database5
execute several SQL queries to extract the required data. These methods populate a series of data structures inPhotosDB
which are then used by the rest of code to create thePhotoInfo
,AlbumInfo
, andFolderInfo
objects. These database structures are ugly...in general, they flatten the SQL database into several different python dicts. The "main" dict is called_dbphotos
. The key is the UUID of the photo and the value is another dict containing details about the photo. Read the code forPhotosDB.__init__()
for additional details. Each of the data structures is documented in the code. - The
PhotoInfo
,AlbumInfo
, andFolderInfo
objects are created as needed. For example,PhotosDB.photos()
returns a list ofPhotoInfo
objects representing the photos in the database andPhotoInfo.album_info
returns a list ofAlbumInfo
objects representing the albums the photo is contained in.
As an example, here's a rough outline of what you'd need to do to add support for a new metadata attribute:
- Reverse engineer Photos.sqlite and develop an sql query that extracts the data
- Add this query to
PhotosDB._process_database5
(assuming a Photos 5 library) and store the data in appropriate data structure accessible through_dbphotos
- Add a property to
PhotoInfo
which accesses_dbphotos
throughself._db
which points to the PhotosDB object. If your change requires significant code, consider creating a separate file for the method -- see _photoinfo_export.py for an example - If exposing the property to the templating system (
PhotoTemplate
andPhotoInfo.render_template()
) add it tophototemplate.py
(which is relatively self-documenting) - If exposing the property to the command line interface, add it to
__main__.py
-- you'll want to look atquery_options
,_query()
,export()
, andexport_photo()
at a minimum to ensure the command line tool can access your new attribute. - Add at least one test to
tests/
! - Ensure all tests pass
python3 -m pytest tests/
- Format code with black
- Submit a pull request :-)