Skip to content

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:

osxphotos classes

  • 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.

Source files

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

How osxphotos works

Broadly, osxphotos works like this:

  1. Opens a readonly copy of photos.db. If the file is locked, a temporary copy will be created then opened.
  2. Reads photos.db to determine which version of Photos created the library (PhotosDB._get_db_version())
  3. If library created with Photos version 2, 3, or 4, PhotosDB._process_database4() processes the database file to extract all required information.
  4. 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, then PhotosDB._process_database5() is called to process the database file and extra all required information.
  5. _process_database4 and _process_database5 execute several SQL queries to extract the required data. These methods populate a series of data structures in PhotosDB which are then used by the rest of code to create the PhotoInfo, AlbumInfo, and FolderInfo 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 for PhotosDB.__init__() for additional details. Each of the data structures is documented in the code.
  6. The PhotoInfo, AlbumInfo, and FolderInfo objects are created as needed. For example, PhotosDB.photos() returns a list of PhotoInfo objects representing the photos in the database and PhotoInfo.album_info returns a list of AlbumInfo objects representing the albums the photo is contained in.

Modifying the code

As an example, here's a rough outline of what you'd need to do to add support for a new metadata attribute:

  1. Reverse engineer Photos.sqlite and develop an sql query that extracts the data
  2. Add this query to PhotosDB._process_database5 (assuming a Photos 5 library) and store the data in appropriate data structure accessible through _dbphotos
  3. Add a property to PhotoInfo which accesses _dbphotos through self._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
  4. If exposing the property to the templating system (PhotoTemplate and PhotoInfo.render_template()) add it to phototemplate.py (which is relatively self-documenting)
  5. If exposing the property to the command line interface, add it to __main__.py -- you'll want to look at query_options, _query(), export(), and export_photo() at a minimum to ensure the command line tool can access your new attribute.
  6. Add at least one test to tests/!
  7. Ensure all tests pass python3 -m pytest tests/
  8. Format code with black
  9. Submit a pull request :-)

home

Clone this wiki locally