### Install Docker Prerequisites Source: https://github.com/rhettbull/osxphotos/blob/main/scripts/README_LINUX_TESTS.md Install Colima, Docker, and docker-buildx using Homebrew. These are necessary for setting up the Docker environment. ```bash brew install colima docker docker-buildx ``` -------------------------------- ### Fish Installation from Pre-Generated File Source: https://github.com/rhettbull/osxphotos/blob/main/osxphotos/share/README.md Install fish completion using a pre-generated file. Copy the file to the fish completions directory. Fish auto-loads completions. ```bash mkdir -p ~/.config/fish/completions cp osxphotos.fish ~/.config/fish/completions/ ``` -------------------------------- ### Install dependencies and osxphotos from git Source: https://github.com/rhettbull/osxphotos/blob/main/README.md Installs required dependencies and osxphotos from a cloned git repository. Recommends using a virtual environment. ```bash python3 -m pip install -r dev_requirements.txt ``` ```bash python3 -m pip install -r requirements.txt ``` ```bash python3 -m pip install -e . ``` -------------------------------- ### Install osxphotos using Homebrew Source: https://github.com/rhettbull/osxphotos/blob/main/README.md Installs osxphotos via Homebrew. First, add the custom tap, then install the package. ```bash # Add the tap brew tap RhetTbull/osxphotos # Install osxphotos brew install osxphotos ``` -------------------------------- ### Install osxphotos in development mode Source: https://github.com/rhettbull/osxphotos/wiki/Setting-up-a-new-development-environment Install the osxphotos package using 'setup.py develop'. This method links the installed package to your development environment, allowing immediate effect of code changes. ```shell python3 setup.py develop ``` -------------------------------- ### Install osxphotos using uv Source: https://github.com/rhettbull/osxphotos/blob/main/README.rst Installs osxphotos using the uv package manager. Ensure uv is installed first. ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` -------------------------------- ### Install osxphotos using pip Source: https://github.com/rhettbull/osxphotos/blob/main/README.md Installs osxphotos directly from PyPI using pip. Use this for direct installation and upgrades. ```bash python3 -m pip install osxphotos ``` ```bash python3 -m pip install --upgrade osxphotos ``` -------------------------------- ### Install Project Requirements Source: https://github.com/rhettbull/osxphotos/blob/main/README_DEV.md Install the necessary Python packages for the project, including development dependencies. ```bash python3 -m pip install -r requirements.txt ``` ```bash python3 -m pip install -r dev_requirements.txt ``` -------------------------------- ### install Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/source/cli.md Installs Python packages into the same environment as osxphotos. Supports upgrading packages and installing from a requirements file. ```APIDOC ## install ### Description Install Python packages into the same environment as osxphotos ### Method ```shell osxphotos install [OPTIONS] [PACKAGES]... ``` ### Options * **-U, --upgrade** Upgrade packages to latest version. * **-r ** Install from requirements file FILE. ### Arguments * **PACKAGES** Optional argument(s) ``` -------------------------------- ### Zsh Installation from Pre-Generated File Source: https://github.com/rhettbull/osxphotos/blob/main/osxphotos/share/README.md Install zsh completion using a pre-generated file. Copy the file to the completion directory and update your .zshrc. Reload your shell configuration. ```bash mkdir -p ~/.local/share/osxphotos/shell-completion cp osxphotos-completion.zsh ~/.local/share/osxphotos/shell-completion/ echo ". ~/.local/share/osxphotos/shell-completion/osxphotos-completion.zsh" >> ~/.zshrc source ~/.zshrc ``` -------------------------------- ### Install osxphotos using pip Source: https://github.com/rhettbull/osxphotos/blob/main/README.rst Installs osxphotos directly from PyPI using pip. Ensure python3 is in your PATH. ```bash python3 -m pip install osxphotos ``` -------------------------------- ### Basic CLI Command Example Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_sources/API_README.rst.txt A simple example of a CLI command using the @query_command decorator. It prints the filename and date of each photo found. Use the --verbose option to see additional debug messages. ```python from __future__ import annotations import osxphotos from osxphotos.cli import query_command, verbose @query_command def example(photos: list[osxphotos.PhotoInfo], **kwargs): """Sample query command for osxphotos. Prints out the filename and date of each photo. Whatever text you put in the function's docstring here, will be used as the command's help text when run via `osxphotos run cli_example_1.py --help` or `python cli_example_1.py --help` """ # verbose() will print to stdout if --verbose option is set # you can optionally provide a level (default is 1) to print only if --verbose is set to that level # for example: -VV or --verbose --verbose == level 2 verbose(f"Found {len(photos)} photo(s)") verbose("This message will only be printed if verbose level 2 is set", level=2) # do something with photos here for photo in photos: # photos is a list of PhotoInfo objects # see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo verbose(f"Processing {photo.original_filename}") print(f"{photo.original_filename} {photo.date}") ... if __name__ == "__main__": # call your function here # you do not need to pass any arguments to the function # as the decorator will handle parsing the command line arguments example() ``` -------------------------------- ### Install Development Requirements Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/README.md Installs necessary Python packages for development. Ensure you are in the correct directory. ```bash python3 -m pip -r dev_requirements.txt ``` -------------------------------- ### Install osxphotos with uv and Python 3.12 Source: https://github.com/rhettbull/osxphotos/blob/main/README.rst Installs the osxphotos tool for a specific Python version using uv. ```bash uv tool install --python 3.12 osxphotos ``` -------------------------------- ### Fish Manual Installation Source: https://github.com/rhettbull/osxphotos/blob/main/osxphotos/share/README.md Manually install fish completion by generating the script and placing it in the fish completions directory. Fish auto-loads completions, but you may need to restart your shell or run fish_update_completions. ```bash # Generate and install _OSXPHOTOS_COMPLETE=fish_source osxphotos > ~/.config/fish/completions/osxphotos.fish # Fish auto-loads completions, just restart your shell or run: fish_update_completions ``` -------------------------------- ### Bash Installation from Pre-Generated File Source: https://github.com/rhettbull/osxphotos/blob/main/osxphotos/share/README.md Install bash completion using a pre-generated file. Copy the file to the completion directory and update your .bashrc. Reload your shell configuration. ```bash mkdir -p ~/.local/share/osxphotos/shell-completion cp osxphotos-completion.bash ~/.local/share/osxphotos/shell-completion/ echo ". ~/.local/share/osxphotos/shell-completion/osxphotos-completion.bash" >> ~/.bashrc source ~/.bashrc ``` -------------------------------- ### Install Shell Completion (Auto-detect) Source: https://github.com/rhettbull/osxphotos/blob/main/osxphotos/share/README.md Automatically detects your shell, generates the completion script, installs it to the XDG-compliant location, and updates your shell configuration. Restart your shell or source your config file after running. ```bash osxphotos shell-completion ``` -------------------------------- ### Install Python Packages Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/source/cli.md Installs Python packages into the same environment as osxphotos. Use --upgrade to get the latest versions or -r to install from a requirements file. ```shell osxphotos install [OPTIONS] [PACKAGES]... ``` -------------------------------- ### iPhotoEventInfo.start_date Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_modules/osxphotos/iphoto.html Gets the start date of the event. ```APIDOC ## iPhotoEventInfo.start_date ### Description Gets the start date of the iPhoto event based on the minimum image date and timezone. ### Method `start_date` (property) ### Parameters None ### Returns datetime.datetime | None - The start date of the event, or None if not available. ``` -------------------------------- ### Display Tutorial Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/source/cli.md Displays the osxphotos tutorial. Optional WIDTH arguments can be provided. ```shell osxphotos tutorial [OPTIONS] [WIDTH]... ``` -------------------------------- ### Start osxphotos REPL Source: https://github.com/rhettbull/osxphotos/blob/main/docs/package_overview.html Starts the osxphotos REPL (Run-Evaluate-Print Loop) for testing and development. This command loads the Photos library and makes various classes, variables, and functions available for interactive use. ```bash osxphotos repl ``` -------------------------------- ### Clone and Set Up osxphotos Development Environment Source: https://github.com/rhettbull/osxphotos/blob/main/tests/README.md Follow these steps to clone the repository, create a virtual environment, activate it, and install the necessary development dependencies and the project itself. ```bash git clone git@github.com:RhetTbull/osxphotos.git cd osxphotos python3 -m venv venv source venv/bin/activate python3 -m pip install -r dev_requirements.txt python3 -m pip install -e . ``` -------------------------------- ### Get Import Session Start Date Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/source/API_README.md Returns the start date of the import session as a datetime object. On Photos <=4, this will be the same as creation_date. ```python import_info.start_date ``` -------------------------------- ### Basic osxphotos Usage Example Source: https://github.com/rhettbull/osxphotos/blob/main/API_README.md Demonstrates initializing PhotosDB, accessing library properties, and querying photos based on keywords and persons. ```python import osxphotos def main(): photosdb = osxphotos.PhotosDB("/Users/smith/Pictures/Photos Library.photoslibrary") print(f"db file = {photosdb.db_path}") print(f"db version = {photosdb.db_version}") print(photosdb.keywords) print(photosdb.persons) print(photosdb.albums) print(photosdb.keywords_as_dict) print(photosdb.persons_as_dict) print(photosdb.albums_as_dict) # find all photos with Keyword = Kids and containing person Katie photos = photosdb.photos(keywords=["Kids"], persons=["Katie"]) print(f"found {len(photos)} photos") # find all photos that include Katie but do not contain the keyword wedding photos = [ p for p in photosdb.photos(persons=["Katie"]) if p not in photosdb.photos(keywords=["wedding"]) ] # get all photos in the database photos = photosdb.photos() for p in photos: print( p.uuid, p.filename, p.date, p.description, p.title, p.keywords, p.albums, p.persons, p.path, p.ismissing, p.hasadjustments, ) if __name__ == "__main__": main() ``` -------------------------------- ### Get iCloud Cloud GUID Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_sources/API_README.rst.txt Returns the unique cloud GUID for a photo if it is stored in iCloud. Returns None for photos not in iCloud. ```python cloud_guid() ``` -------------------------------- ### Get Help for a Specific osxphotos Command Source: https://github.com/rhettbull/osxphotos/blob/main/README.md To get detailed help for a specific command, append the command name to 'osxphotos help'. For example, 'osxphotos help export' provides information on the export command. ```bash Usage: osxphotos export [OPTIONS] ... DEST Export photos from the Photos database. Export path DEST is required. Optionally, query the Photos database using 1 or more search options; if more than one option is provided, they are treated as "AND" (e.g. search for photos matching all options). If no query options are provided, all photos will be exported. By default, all versions of all photos will be exported including edited versions, live photo movies, burst photos, and associated raw images. See --skip-edited, --skip-live, --skip-bursts, and --skip-raw ``` -------------------------------- ### Set Up Virtual Environment Source: https://github.com/rhettbull/osxphotos/blob/main/README_DEV.md Create and activate a Python virtual environment for development. This isolates project dependencies. ```bash python3 -m venv venv ``` ```bash source venv/bin/activate ``` -------------------------------- ### Get Album Start Date Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/source/API_README.md Returns the date of the earliest photo in the album as a timezone-aware datetime object. ```python album.start_date ``` -------------------------------- ### Create a Query Command with osxphotos Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_sources/API_README.rst.txt This example demonstrates how to create a custom command-line tool using the `@query_command` decorator. It shows how to process a list of photos, use a key-value store for state management, and provide verbose and error messages. Use this for commands that operate on all photos or a subset defined by query options. ```python from __future__ import annotations import datetime import click import osxphotos from osxphotos.cli import ( abort, echo, echo_error, kvstore, logger, query_command, verbose, ) @query_command() @click.option( "--resume", is_flag=True, help="Resume processing from last run, do not reprocess photos", ) @click.option( "--dry-run", is_flag=True, help="Do a dry run, don't actually do anything" ) def example(resume, dry_run, photos: list[osxphotos.PhotoInfo], **kwargs): """Sample query command for osxphotos. Prints out the filename and date of each photo. Whatever text you put in the function's docstring here, will be used as the command's help text when run via `osxphotos run cli_example_2.py --help` or `python cli_example_2.py --help` The @query_command decorator returns a click.command so you can add additional options using standard click decorators. For example, the --resume and --dry-run options. For more information on click, see https://palletsprojects.com/p/click/. """ # abort will print the message to stderr and exit with the given exit code if not photos: abort("Nothing to do!", 1) # verbose() will print to stdout if --verbose option is set # you can optionally provide a level (default is 1) to print only if --verbose is set to that level # for example: -VV or --verbose --verbose == level 2 verbose(f"Found [count]{len(photos)}[/] photos") verbose("This message will only be printed if verbose level 2 is set", level=2) # the logger is a python logging.Logger object # debug messages will only be printed if --debug option is set logger.debug(f"{kwargs=}") # kvstore() returns a SQLiteKVStore object for storing state between runs # this is basically a persistent dictionary that can be used to store state # see https://github.com/RhetTbull/sqlitekvstore for more information kv = kvstore("cli_example_2") verbose(f"Using key-value cache: {kv.path}") # do something with photos here for photo in photos: # photos is a list of PhotoInfo objects # see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo if resume and photo.uuid in kv: echo( f"Skipping processed photo [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])" ) continue # store the uuid and current time in the kvstore # the key and value must be a type supported by SQLite: int, float, str, bytes, bool, None # if you need to store other values, you should serialize them to a string or bytes first # for example, using json.dumps() or pickle.dumps() kv[photo.uuid] = datetime.datetime.now().isoformat() echo(f"Processing [filename]{photo.original_filename}[/] [time]{photo.date}[/]") if not dry_run: # do something with the photo here echo(f"Doing something with [filename]{photo.original_filename}[/]") # echo_error will print to stderr # if you add [warning] or [error], it will be formatted accordingly # and include an emoji to make the message stand out echo_error("[warning]This is a warning message!") echo_error("[error]This is an error message!") if __name__ == "__main__": # call your function here # you do not need to pass any arguments to the function # as the decorator will handle parsing the command line arguments example() ``` -------------------------------- ### Get ExifTool Path Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_sources/API_README.rst.txt Retrieves the path to the ExifTool executable. This is useful for verifying ExifTool installation and accessibility. ```python >>> osxphotos.exiftool.get_exiftool_path() '/usr/local/bin/exiftool' >>> ``` -------------------------------- ### Get ExifTool Path Source: https://github.com/rhettbull/osxphotos/blob/main/docs/API_README.html Check if exiftool is installed and accessible in the system's PATH. Raises FileNotFoundError if not found. ```python import osxphotos # Check if exiftool is installed and get its path print(osxphotos.exiftool.get_exiftool_path()) ``` -------------------------------- ### Example osxphotos Query Command Source: https://github.com/rhettbull/osxphotos/blob/main/API_README.md This example demonstrates how to create a command-line tool using the `@query_command` decorator. It includes options for resuming processing and performing dry runs. The function processes a list of `PhotoInfo` objects, utilizing helper functions like `verbose`, `logger`, and `kvstore` for output and state management. Use this when you need to build custom CLI tools that interact with photos based on queries. ```python from __future__ import annotations import datetime import click import osxphotos from osxphotos.cli import ( abort, echo, echo_error, kvstore, logger, query_command, verbose, ) @query_command() @click.option( "--resume", is_flag=True, help="Resume processing from last run, do not reprocess photos", ) @click.option( "--dry-run", is_flag=True, help="Do a dry run, don't actually do anything" ) def example(resume, dry_run, photos: list[osxphotos.PhotoInfo], **kwargs): """Sample query command for osxphotos. Prints out the filename and date of each photo. Whatever text you put in the function's docstring here, will be used as the command's help text when run via `osxphotos run cli_example_2.py --help` or `python cli_example_2.py --help` The @query_command decorator returns a click.command so you can add additional options using standard click decorators. For example, the --resume and --dry-run options. For more information on click, see https://palletsprojects.com/p/click/. """ # abort will print the message to stderr and exit with the given exit code if not photos: abort("Nothing to do!", 1) # verbose() will print to stdout if --verbose option is set # you can optionally provide a level (default is 1) to print only if --verbose is set to that level # for example: -VV or --verbose --verbose == level 2 verbose(f"Found [count]{len(photos)}[/] photos") verbose("This message will only be printed if verbose level 2 is set", level=2) # the logger is a python logging.Logger object # debug messages will only be printed if --debug option is set logger.debug(f"{kwargs=}") # kvstore() returns a SQLiteKVStore object for storing state between runs # this is basically a persistent dictionary that can be used to store state # see https://github.com/RhetTbull/sqlitekvstore for more information kv = kvstore("cli_example_2") verbose(f"Using key-value cache: {kv.path}") # do something with photos here for photo in photos: # photos is a list of PhotoInfo objects # see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo if resume and photo.uuid in kv: echo( f"Skipping processed photo [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])" ) continue # store the uuid and current time in the kvstore # the key and value must be a type supported by SQLite: int, float, str, bytes, bool, None # if you need to store other values, you should serialize them to a string or bytes first # for example, using json.dumps() or pickle.dumps() kv[photo.uuid] = datetime.datetime.now().isoformat() echo(f"Processing [filename]{photo.original_filename}[/] [time]{photo.date}[/]") if not dry_run: # do something with the photo here ``` -------------------------------- ### Get ExifTool Path Source: https://github.com/rhettbull/osxphotos/blob/main/API_README.md Checks if exiftool is installed and returns its path. Logs a warning and returns None if not found. ```python >>> import osxphotos >>> osxphotos.exiftool.get_exiftool_path() '/usr/local/bin/exiftool' >>> ``` -------------------------------- ### Build Advanced CLI Tool with osxphotos (Dry Run/Resume) Source: https://github.com/rhettbull/osxphotos/blob/main/API_README.md This example demonstrates building a more complex command-line tool using osxphotos, incorporating features like 'dry run' and 'resume' capabilities to preserve state across multiple executions. It leverages the built-in helper functions for efficient implementation. ```python """Sample query command for osxphotos This shows how simple it is to create a command line tool using osxphotos to process your photos. Using the @query_command decorator turns your function to a full-fledged command line app that can be run via `osxphotos run cli_example_2.py` or `python cli_example_2.py` if you have pip installed osxphotos. Using this decorator makes it very easy to create a quick command line tool that can operate on a subset of your photos. Additionally, writing a command in this way makes it easy to later incorporate the command into osxphotos as a full-fledged command. The decorator will add all the query options available in `osxphotos query` as command line options ``` -------------------------------- ### Get All EXIF Metadata as Dictionary Source: https://github.com/rhettbull/osxphotos/blob/main/API_README.md Retrieves all EXIF metadata from a photo file as a dictionary. Use tag_groups=False to omit group names from keys. Requires exiftool to be installed. ```python {'Composite:Aperture': 2.2, 'Composite:GPSPosition': '-34.9188916666667 138.596861111111', 'Composite:ImageSize': '2754 2754', 'EXIF:CreateDate': '2017:06:20 17:18:56', 'EXIF:LensMake': 'Apple', 'EXIF:LensModel': 'iPhone 6s back camera 4.15mm f/2.2', 'EXIF:Make': 'Apple', 'XMP:Title': 'Elder Park', } ``` -------------------------------- ### Template Formatting Examples Source: https://github.com/rhettbull/osxphotos/blob/main/docs/cli.html Demonstrates different ways to format folder and album names within templates. Use these to control how path separators and delimiters are rendered. ```shell {folder_album} ``` ```shell {folder_album(>)} ``` ```shell {folder_album()} ``` -------------------------------- ### Get Full Address String Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/source/API_README.md Returns the complete postal address as a single string if available. For example: '2038 18th St NW, Washington, DC 20009, United States'. ```APIDOC ## Get Full Address String ### Description Returns the full postal address as a string if defined, otherwise `None`. ### Method `photo.place.address_str` ### Returns - `str` or `None`: The full postal address. ``` -------------------------------- ### Basic osxphotos CLI Command Example Source: https://github.com/rhettbull/osxphotos/blob/main/docs/API_README.html A sample osxphotos command-line tool demonstrating basic photo processing. It uses @query_command to access photos and click options for resume and dry-run functionality. Includes usage of helper functions like abort, echo, verbose, logger, and kvstore for user feedback and state management. ```python from __future__ import annotations import datetime import click import osxphotos from osxphotos.cli import ( abort, echo, echo_error, kvstore, logger, query_command, verbose, ) @query_command() @click.option( "--resume", is_flag=True, help="Resume processing from last run, do not reprocess photos", ) @click.option( "--dry-run", is_flag=True, help="Do a dry run, don't actually do anything" ) def example(resume, dry_run, photos: list[osxphotos.PhotoInfo], **kwargs): """Sample query command for osxphotos. Prints out the filename and date of each photo. Whatever text you put in the function's docstring here, will be used as the command's help text when run via `osxphotos run cli_example_2.py --help` or `python cli_example_2.py --help` The @query_command decorator returns a click.command so you can add additional options using standard click decorators. For example, the --resume and --dry-run options. For more information on click, see https://palletsprojects.com/p/click/. """ # abort will print the message to stderr and exit with the given exit code if not photos: abort("Nothing to do!", 1) # verbose() will print to stdout if --verbose option is set # you can optionally provide a level (default is 1) to print only if --verbose is set to that level # for example: -VV or --verbose --verbose == level 2 verbose(f"Found [count]{len(photos)}[/] photos") verbose("This message will only be printed if verbose level 2 is set", level=2) # the logger is a python logging.Logger object # debug messages will only be printed if --debug option is set logger.debug(f"{kwargs=}") # kvstore() returns a SQLiteKVStore object for storing state between runs # this is basically a persistent dictionary that can be used to store state # see https://github.com/RhetTbull/sqlitekvstore for more information kv = kvstore("cli_example_2") verbose(f"Using key-value cache: {kv.path}") # do something with photos here for photo in photos: # photos is a list of PhotoInfo objects # see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo if resume and photo.uuid in kv: echo( f"Skipping processed photo [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])" ) continue # store the uuid and current time in the kvstore # the key and value must be a type supported by SQLite: int, float, str, bytes, bool, None # if you need to store other values, you should serialize them to a string or bytes first # for example, using json.dumps() or pickle.dumps() kv[photo.uuid] = datetime.datetime.now().isoformat() echo(f"Processing [filename]{photo.original_filename}[/] [time]{photo.date}[/]") if not dry_run: # do something with the photo here ``` -------------------------------- ### Combining Template Fields Source: https://github.com/rhettbull/osxphotos/blob/main/osxphotos/phototemplate.cog.md Combine multiple template fields to return a list of values. This example shows how to get the creation year and album name, with nested combine operators for multiple values. ```cog {created.year&{folder_album,}} ``` ```cog {template1&{template2&{template3,},},} ``` -------------------------------- ### Get Paths of All Raw Photos Source: https://github.com/rhettbull/osxphotos/blob/main/API_README.md Iterates through all photos in the database and prints the path of each raw photo, whether it's a single raw import or part of a raw+JPEG pair. Requires the osxphotos library to be installed. ```python >>> import osxphotos >>> photosdb = osxphotos.PhotosDB() >>> photos = photosdb.photos() >>> all_raw = [p for p in photos if p.israw or p.has_raw] >>> for raw in all_raw: ... path = raw.path if raw.israw else raw.path_raw ... print(path) ``` -------------------------------- ### Build Simple Command Line Tools with osxphotos Source: https://github.com/rhettbull/osxphotos/blob/main/docs/API_README.html Demonstrates how to create a command-line tool using the @selection_command decorator. This decorator automatically handles argument parsing and passes selected PhotoInfo objects to the decorated function. Use verbose() for conditional output and print() for standard output. ```python from __future__ import annotations import osxphotos from osxphotos.cli import selection_command, verbose @selection_command def example(photos: list[osxphotos.PhotoInfo], **kwargs): """Sample command for osxphotos. Prints out the filename and date of each photo currently selected in Photos.app. Whatever text you put in the function's docstring here, will be used as the command's help text when run via `osxphotos run cli_example_1.py --help` or `python cli_example_1.py --help` """ # verbose() will print to stdout if --verbose option is set # you can optionally provide a level (default is 1) to print only if --verbose is set to that level # for example: -VV or --verbose --verbose == level 2 verbose(f"Found {len(photos)} photo(s)") verbose("This message will only be printed if verbose level 2 is set", level=2) # do something with photos here for photo in photos: # photos is a list of PhotoInfo objects # see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo verbose(f"Processing {photo.original_filename}") print(f"{photo.original_filename} {photo.date}") ... if __name__ == "__main__": # call your function here # you do not need to pass any arguments to the function # as the decorator will handle parsing the command line arguments example() ``` -------------------------------- ### FaceInfo.__init__() Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_modules/osxphotos/personinfo.html Initializes a new FaceInfo instance. ```APIDOC ## FaceInfo.__init__() ### Description Initializes a new FaceInfo instance with information about a face detected in the Photos library. ### Arguments - `db` (PhotosDB, optional): An instance of the PhotosDB object. - `pk` (int, optional): The primary key of the face to initialize the object with. ``` -------------------------------- ### Install osxphotos using uv Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_sources/overview.rst.txt Installs osxphotos using the uv package manager. Ensure uv is installed first. This command installs osxphotos for Python 3.12. ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` ```bash uv self update ``` ```bash uv tool install --python 3.12 osxphotos ``` -------------------------------- ### Set up a virtual environment for osxphotos on Linux Source: https://github.com/rhettbull/osxphotos/blob/main/README.rst Creates and activates a Python virtual environment to manage osxphotos installation on Linux, avoiding potential conflicts with system packages. ```bash python3 -m venv .venv-osxphotos source .venv-osxphotos/bin/activate ``` -------------------------------- ### Verify ExifTool Installation Source: https://context7.com/rhettbull/osxphotos/llms.txt Checks if the exiftool executable is installed and accessible. If not, it prints a message with installation instructions. ```python import osxphotos from osxphotos.exiftool import ExifTool, get_exiftool_path # Verify exiftool is installed try: path = get_exiftool_path() print(f"exiftool found at: {path}") except FileNotFoundError: print("exiftool not installed — install from https://exiftool.org/") ``` -------------------------------- ### Simple Command Line Tool with @query_command Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/source/API_README.md Demonstrates building a basic command-line tool using the @query_command decorator. This tool can be executed directly and handles command-line argument parsing automatically. It includes examples of printing to standard output and standard error with formatting. ```python from osxphotos.cli import query_command, echo, echo_error @query_command def example(photos): """Sample command for osxphotos. This command will be called with a list of PhotoInfo objects representing the photos that match the query criteria. The decorator handles all argument parsing. """ # echo will print to stdout # you can use f-strings to include photo attributes for photo in photos: echo(f"Doing something with {photo.original_filename}") # echo_error will print to stderr # if you add [warning] or [error], it will be formatted accordingly # and include an emoji to make the message stand out echo_error("[warning]This is a warning message!") echo_error("[error]This is an error message!") if __name__ == "__main__": # call your function here # you do not need to pass any arguments to the function # as the decorator will handle parsing the command line arguments example() ``` -------------------------------- ### Create Configuration and PhotoInfo Tables Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_modules/osxphotos/export_db.html Creates the 'config' and 'photoinfo' tables for storing application settings and photo metadata. ```sql CREATE TABLE IF NOT EXISTS config ( id INTEGER PRIMARY KEY, datetime TEXT, config TEXT ); ``` ```sql CREATE TABLE IF NOT EXISTS photoinfo ( id INTEGER PRIMARY KEY, uuid TEXT NOT NULL, photoinfo JSON, UNIQUE(uuid) ); ``` -------------------------------- ### Build Package and Executable Source: https://github.com/rhettbull/osxphotos/blob/main/README_DEV.md Run the build script to create the package and executable. This is typically only for maintainers during releases. ```bash ./build.sh ``` -------------------------------- ### Description Formatting Example Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_sources/API_README.rst.txt Shows how to apply title case formatting to a photo's description. ```text "My Description" ``` -------------------------------- ### Install osxphotos on Linux with venv Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_sources/overview.cog.rst.txt Installs osxphotos within a Python virtual environment on Linux, which can help resolve installation issues. ```bash python3 -m venv .venv-osxphotos source .venv-osxphotos/bin/activate python3 -m pip install osxphotos ``` -------------------------------- ### Zsh Manual Installation Source: https://github.com/rhettbull/osxphotos/blob/main/osxphotos/share/README.md Manually install zsh completion by creating directories, generating the script, and adding it to your .zshrc. Reload your shell configuration after installation. ```bash # Create directory and generate completion mkdir -p ~/.local/share/osxphotos/shell-completion _OSXPHOTOS_COMPLETE=zsh_source osxphotos > ~/.local/share/osxphotos/shell-completion/osxphotos-completion.zsh # Add to your ~/.zshrc echo ". ~/.local/share/osxphotos/shell-completion/osxphotos-completion.zsh" >> ~/.zshrc # Reload source ~/.zshrc ``` -------------------------------- ### Initialize and Query PhotosDB Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_sources/API_README.rst.txt Demonstrates how to initialize a PhotosDB object with a specific library path and access its properties like database path, version, keywords, persons, and albums. It also shows how to find photos based on keywords and persons, and how to iterate through all photos in the database, printing various attributes for each. ```python import osxphotos def main(): photosdb = osxphotos.PhotosDB("/Users/smith/Pictures/Photos Library.photoslibrary") print(f"db file = {photosdb.db_path}") print(f"db version = {photosdb.db_version}") print(photosdb.keywords) print(photosdb.persons) print(photosdb.albums) print(photosdb.keywords_as_dict) print(photosdb.persons_as_dict) print(photosdb.albums_as_dict) # find all photos with Keyword = Kids and containing person Katie photos = photosdb.photos(keywords=["Kids"], persons=["Katie"]) print(f"found {len(photos)} photos") # find all photos that include Katie but do not contain the keyword wedding photos = [ p for p in photosdb.photos(persons=["Katie"]) if p not in photosdb.photos(keywords=["wedding"]) ] # get all photos in the database photos = photosdb.photos() for p in photos: print( p.uuid, p.filename, p.date, p.description, p.title, p.keywords, p.albums, p.persons, p.path, p.ismissing, p.hasadjustments, ) if __name__ == "__main__": main() ``` -------------------------------- ### Bash Manual Installation Source: https://github.com/rhettbull/osxphotos/blob/main/osxphotos/share/README.md Manually install bash completion by creating directories, generating the script, and adding it to your .bashrc or .bash_profile. Reload your shell configuration after installation. ```bash # Create directory and generate completion mkdir -p ~/.local/share/osxphotos/shell-completion _OSXPHOTOS_COMPLETE=bash_source osxphotos > ~/.local/share/osxphotos/shell-completion/osxphotos-completion.bash # Add to your ~/.bashrc (or ~/.bash_profile on macOS) echo ". ~/.local/share/osxphotos/shell-completion/osxphotos-completion.bash" >> ~/.bashrc # Reload source ~/.bashrc ``` -------------------------------- ### Build HTML Documentation Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/README.md Builds the HTML version of the documentation. Navigate to the 'docsrc' directory before running. ```bash cd docsrc make html ``` -------------------------------- ### Initialize and Query PhotosDB Source: https://github.com/rhettbull/osxphotos/blob/main/API_README.md Example of initializing a PhotosDB object and querying all photos, then analyzing shared and hidden photos, and filtering for movies. This demonstrates library statistics and filtering capabilities. ```python >>> import osxphotos >>> photosdb = osxphotos.PhotosDB("/Users/smith/Pictures/Photos Library.photoslibrary") >>> photos = photosdb.photos() >>> len(photos) 25002 >>> shared = [p for p in photos if p.shared] >>> len(shared) 5609 >>> not_shared = [p for p in photos if not p.shared] >>> len(not_shared) 19393 >>> hidden = [p for p in photos if p.hidden] >>> len(hidden) 7 >>> movies = photosdb.photos(movies=True, images=False) >>> len(movies) 625 >>> shared_movies = [m for m in movies if m.shared] >>> len(shared_movies) 151 >>> ``` -------------------------------- ### Install osxphotos using uv Source: https://github.com/rhettbull/osxphotos/blob/main/README.md Installs osxphotos using the uv package manager. Use this command to install the latest version or a specific Python version. ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` ```bash uv self update ``` ```bash uv tool install --python 3.13 osxphotos ``` ```bash uv tool install osxphotos --python 3.11 ``` ```bash uv tool upgrade osxphotos ``` ```bash uv tool run --python 3.13 osxphotos ``` ```bash uvx --python 3.13 osxphotos ``` ```bash uv tool run --python 3.11 osxphotos ``` ```bash uvx --python 3.11 osxphotos ``` -------------------------------- ### Update Installation Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/source/cli.md Updates the osxphotos installation to the latest version. ```shell osxphotos update [OPTIONS] ``` -------------------------------- ### Initialize PhotosDB with custom database and library paths Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_modules/osxphotos/photosdb/photosdb.html Use this when the Photos database file is not in its default location within the library bundle. Set `dbfile` to the path of the `Photos.sqlite` file and `library_path` to the root of the Photos library bundle. ```python photosdb = PhotosDB(dbfile="/path/to/database/Photos.sqlite", library_path="/path/to/Photos Library.photoslibrary") ``` -------------------------------- ### Install Python Packages with osxphotos CLI Source: https://github.com/rhettbull/osxphotos/blob/main/docs/package_overview.html Installs additional Python packages into the same virtual environment as osxphotos using the `osxphotos install` command. This is analogous to using `pip`. ```bash osxphotos install ``` -------------------------------- ### Start Process with Timeout Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_modules/osxphotos/exiftool.html Starts a subprocess with a specified timeout. Raises TimeoutError or RuntimeError if the process fails to start within the given time. Handles exceptions during process startup. ```python def _start_process_with_timeout(args, timeout, **kwargs): process = None exception = None thread = threading.Thread(target=target) thread.daemon = True thread.start() thread.join(timeout) if thread.is_alive(): # Thread is still running, startup timed out # Note: We can't easily kill the thread, but the daemon flag # means it will be cleaned up when the main process exits raise TimeoutError(f"process startup timed out after {timeout} seconds") if exception: raise exception if process is None: raise RuntimeError("Failed to start process for unknown reason") return process ``` -------------------------------- ### Initialize Takeout Archive Browser Source: https://github.com/rhettbull/osxphotos/blob/main/tests/test-images/Takeout/archive_browser.html Sets up the Takeout archive browser by initializing service tiles, folders, and tabs. It also triggers the display of the first service by default. ```javascript var takeout = new Object(); takeout.SERVICES = [ 'PHOTOS' ]; takeout.getServiceTile = function(service) { return $('#service-tile-' + service); }; takeout.getServiceDetails = function(service) { return $('#service-details-' + service); }; takeout.selectService = function(event) { var selectedService = event.data; for (var i = 0; i < takeout.SERVICES.length; i++) { var currentService = takeout.SERVICES[i]; if (currentService == selectedService) { var details = takeout.getServiceDetails(currentService); details.show(); takeout.getServiceTile(currentService).addClass('selected'); } else { takeout.getServiceDetails(currentService).hide(); takeout.getServiceTile(currentService).removeClass('selected'); } } }; takeout.toggleFolder = function(event) { var folder = $(this); var folderSelected = !folder.data('selected'); folder.data('selected', folderSelected); if (folderSelected) { folder.siblings('div.extracted-child').show(); } else { folder.siblings('div.extracted-child').hide(); } }; takeout.selectTab = function(event) { $(this).closest('.service-detail').find('.tab-content').hide(); $(this).closest('.service-detail').find('.selected').removeClass('selected'); $(this).closest('.service-detail').find($(this).data('selector')).show(); $(this).addClass('selected'); }; $(function() { for (var i = 0; i < takeout.SERVICES.length; i++) { var currentService = takeout.SERVICES[i]; takeout.getServiceTile(currentService).click(currentService, takeout.selectService); } var folders = $('button.extracted-folder'); folders.data('selected', false); folders.click(takeout.toggleFolder); $('div.extracted-child').hide(); $('.tab').click(takeout.selectTab); $('.about-tab').data('selector', '.service-about'); $('.about-tab').addClass('selected'); $('.files-tab').data('selector', '.service-files'); $('.service-files').hide(); $('.formats-tab').data('selector', '.service-formats'); $('.service-formats').hide(); // Trigger a click on the first service tile to load the first service when the page // opens. takeout.getServiceTile(takeout.SERVICES[0]).trigger("click"); }); ``` -------------------------------- ### Install Shell Completion Source: https://github.com/rhettbull/osxphotos/blob/main/docsrc/source/cli.md Installs tab completion for bash, zsh, and fish shells. The shell type is auto-detected if not specified. Restart your shell or source your shell config file after installation. ```shell osxphotos shell-completion [OPTIONS] [[bash|zsh|fish]] ``` -------------------------------- ### Initialize iPhoto Library Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_modules/osxphotos/iphoto.html Initializes the iPhoto library by setting the library path and validating its existence. It also sets up database paths and verbose logging, preparing for data loading. ```python self.library_path: pathlib.Path = pathlib.Path(dbfile).absolute() if not self.library_path.is_dir(): raise FileNotFoundError(f"Invalid iPhoto library path: {self.library_path}") if not self.library_path.joinpath("Database").is_dir(): raise FileNotFoundError(f"Invalid iPhoto library path: {self.library_path}") self._library_path = str(self.library_path) # compatibility with PhotosDB # for compatibility with PhotosDB but not a 1:1 mapping as iPhoto uses several databases self.db_path = str(self.library_path.joinpath("Database/apdb/Library.apdb")) if verbose is None: verbose = noop elif not callable(verbose): raise TypeError("verbose must be callable") self.verbose = verbose self._verbose = self.verbose # compatibility with PhotosDB self._rich = rich # currently unused, compatibility with PhotosDB self._exiftool_path = exiftool # initialize database dictionaries self._db_photos = {} # mapping of uuid to photo data self._dbphotos = self._db_photos # for compatability with PhotosDB self._db_event_notes = {} # mapping of modelId to event notes self._db_places = {} # mapping of modelId to places self._db_properties = {} # mapping of versionId to properties self._db_exif_info = {} # mapping of versionId to EXIF info self._db_persons = {} # mapping of modelId to persons self._db_faces = {} # mapping of modelID to face info self._db_faces_edited = {} # mapping of modelID to face info for edited photos self._db_folders = {} # mapping of modelId to folders self._db_albums = {} # mapping of modelId to albums self._db_volumes = {} # mapping of volume uuid to volume name # set _db_version, _photos_ver, _model_ver even though they're not used in iPhoto because other code depends on these self._db_version = _IPHOTO_VERSION self._photos_ver = 0 self._model_ver = 0 self._load_library() self._source = "iPhoto" ``` -------------------------------- ### Installing Python Packages with osxphotos CLI Source: https://github.com/rhettbull/osxphotos/blob/main/docs/_sources/package_overview.rst.txt Shows how to use the 'osxphotos install' command to install additional Python packages into the osxphotos virtual environment. This is necessary if your custom scripts require external libraries. ```bash osxphotos install package_name ``` -------------------------------- ### Simple osxphotos Package Usage Source: https://github.com/rhettbull/osxphotos/blob/main/docs/package_overview.html Demonstrates basic usage of the osxphotos package to access library metadata like keywords, persons, and album names. Also shows how to find photos based on keywords and persons. ```python """ 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() ```