OSXPhotos Python Package Overview

Example uses of the OSXPhotos python package

""" Simple usage of the package """
import osxphotos

def main():
    photosdb = osxphotos.PhotosDB()
    print(photosdb.keywords)
    print(photosdb.persons)
    print(photosdb.album_names)

    print(photosdb.keywords_as_dict)
    print(photosdb.persons_as_dict)
    print(photosdb.albums_as_dict)

    # find all photos with Keyword = Foo and containing John Smith
    photos = photosdb.photos(keywords=["Foo"],persons=["John Smith"])

    # find all photos that include Alice Smith but do not contain the keyword Bar
    photos = [p for p in photosdb.photos(persons=["Alice Smith"])
                if p not in photosdb.photos(keywords=["Bar"]) ]
    for p in photos:
        print(
            p.uuid,
            p.filename,
            p.original_filename,
            p.date,
            p.description,
            p.title,
            p.keywords,
            p.albums,
            p.persons,
            p.path,
        )

if __name__ == "__main__":
    main()
""" Export all photos to specified directory using album names as folders
    If file has been edited, also export the edited version,
    otherwise, export the original version
    This will result in duplicate photos if photo is in more than album """

import os.path
import pathlib
import sys

import click
from pathvalidate import is_valid_filepath, sanitize_filepath

import osxphotos


@click.command()
@click.argument("export_path", type=click.Path(exists=True))
@click.option(
    "--default-album",
    help="Default folder for photos with no album. Defaults to 'unfiled'",
    default="unfiled",
)
@click.option(
    "--library-path",
    help="Path to Photos library, default to last used library",
    default=None,
)
def export(export_path, default_album, library_path):
    export_path = os.path.expanduser(export_path)
    library_path = os.path.expanduser(library_path) if library_path else None

    if library_path is not None:
        photosdb = osxphotos.PhotosDB(library_path)
    else:
        photosdb = osxphotos.PhotosDB()

    photos = photosdb.photos()

    for p in photos:
        if not p.ismissing:
            albums = p.albums
            if not albums:
                albums = [default_album]
            for album in albums:
                click.echo(f"exporting {p.filename} in album {album}")

                # make sure no invalid characters in destination path (could be in album name)
                album_name = sanitize_filepath(album, platform="auto")

                # create destination folder, if necessary, based on album name
                dest_dir = os.path.join(export_path, album_name)

                # verify path is a valid path
                if not is_valid_filepath(dest_dir, platform="auto"):
                    sys.exit(f"Invalid filepath {dest_dir}")

                # create destination dir if needed
                if not os.path.isdir(dest_dir):
                    os.makedirs(dest_dir)

                # export the photo
                if p.hasadjustments:
                    # export edited version
                    exported = p.export(dest_dir, edited=True)
                    edited_name = pathlib.Path(p.path_edited).name
                    click.echo(f"Exported {edited_name} to {exported}")
                # export unedited version
                exported = p.export(dest_dir)
                click.echo(f"Exported {p.filename} to {exported}")
        else:
            click.echo(f"Skipping missing photo: {p.filename}")


if __name__ == "__main__":
    export()

OSXPhotos REPL

The osxphotos command line interface includes a REPL (Run-Evaluate-Print Loop) for testing and development.

The REPL is started with the command: osxphotos repl:

$ osxphotos repl
python version: 3.10.2 (main, Feb  2 2022, 07:36:01) [Clang 12.0.0 (clang-1200.0.32.29)]
osxphotos version: 0.47.10
Using last opened Photos library: /Users/user/Pictures/Photos Library.photoslibrary
Loading database
Processing database /Users/user/Pictures/Photos Library.photoslibrary/database/photos.db
Processing database /Users/user/Pictures/Photos Library.photoslibrary/database/Photos.sqlite
Database locked, creating temporary copy.
Processing database.
Database version: 6000, 5.
Processing persons in photos.
Processing detected faces in photos.
Processing albums.
Processing keywords.
Processing photo details.
Processing import sessions.
Processing additional photo details.
Processing face details.
Processing photo labels.
Processing EXIF details.
Processing computed aesthetic scores.
Processing comments and likes for shared photos.
Processing moments.
Done processing details from Photos library.
Done: took 18.54 seconds
Getting photos
Found 31581 photos in 0.77 seconds

The following classes have been imported from osxphotos:
- AlbumInfo, ExifTool, PhotoInfo, PhotoExporter, ExportOptions, ExportResults, PhotosDB, PlaceInfo, QueryOptions, MomentInfo, ScoreInfo, SearchInfo

The following variables are defined:
- photosdb: PhotosDB() instance for '/Users/user/Pictures/Photos Library.photoslibrary'
- photos: list of PhotoInfo objects for all photos filtered with any query options passed on command line (len=31581)
- all_photos: list of PhotoInfo objects for all photos in photosdb, including those in the trash (len=31581)
- selected: list of PhotoInfo objects for any photos selected in Photos (len=0)

The following functions may be helpful:
- get_photo(uuid): return a PhotoInfo object for photo with uuid; e.g. get_photo('B13F4485-94E0-41CD-AF71-913095D62E31')
- get_selected(); return list of PhotoInfo objects for photos selected in Photos
- show(photo): open a photo object in the default viewer; e.g. show(selected[0])
- show(path): open a file at path in the default viewer; e.g. show('/path/to/photo.jpg')
- spotlight(photo): open a photo and spotlight it in Photos
- inspect(object): print information about an object; e.g. inspect(PhotoInfo)
- explore(object): interactively explore an object with objexplore; e.g. explore(PhotoInfo)
- q, quit, quit(), exit, exit(): exit this interactive shell

>>>

Using the osxphotos CLI to run python code

The osxphotos CLI can also be used to run your own python code using the osxphotos run command.

This is useful if you have installed the CLI using pipx but want to use the osxphotos programmatic interface in your own scripts.

If you need to install any additional python packages to use in your own scripts, you can use the osxphotos install command which installs python packages just as pip does but into the same virtual environment that osxphotos is installed in.

Likewise, you can use osxphotos uninstall to uninstall any installed python packages.

These features are also useful for developing custom functions to use with --query-function and --post-function as well as {function} templates and function: filters.