Ein Anderssono 479c284dfc v0.2.4: stop export loop on Ctrl+C instead of flooding failures
- Add IsCancelled() to Bridge interface
- Check bridge.IsCancelled() before each export in serial/parallel/backupTree
- Parallel workers mark remaining slots as 'cancelled' instead of exporting
- Add photos_request_is_cancelled to ObjC and C stub
2026-06-11 21:44:55 +02:00

photoscli

photoscli is a small macOS-only CLI written in Go that reads data from Apple Photos through a PhotoKit bridge.

It supports five tasks:

  • listing albums
  • listing asset IDs, filenames, and cloud status in an album
  • showing the folder and album tree
  • backing up all albums into the Photos folder tree
  • exporting resized JPEG previews or original files from an album

What The Code Does

The executable lives in cmd/photoscli and calls into internal/photos, which wraps an Objective-C bridge in bridge/.

Current behavior:

  • albums prints one line per album as <album-id>\t<title>
  • photos --album-id <id-or-title> prints one asset per line as <id>\t<filename>\t<cloud>; accepts either a PhotoKit local identifier or an album title
  • tree prints the human-readable folder and album hierarchy from Apple Photos
  • backup-all --out <dir> [--size <px>] [--originals] exports every album into a matching folder tree, showing per-asset progress with filename, size, and cloud status
  • export --album-id <id-or-title> --out <dir> [--size <px>] [--originals] exports either JPEG previews or original files with a progress bar showing filename, size, and cloud status; --album-id accepts either a PhotoKit local identifier or an album title

The bridge uses PhotoKit to:

  • request access to the user's Photos library
  • fetch album collections by local identifier or album title
  • fetch album assets sorted by creationDate ascending
  • render resized images with PHImageManager
  • write JPEG files with compression 0.85
  • support graceful cancellation via a cancel flag checked between file exports

Requirements

  • macOS
  • Go 1.22+
  • Xcode command-line tools

The project builds with cgo and links against Photos, Foundation, and AppKit.

Build

make build

Output binary:

./bin/photoscli

Test

make test

Tests run against a stub bridge, so they do not require real Photos access.

Usage

./bin/photoscli albums
./bin/photoscli photos --album-id "<album-local-identifier>"
./bin/photoscli photos --album-id "Vacation"
./bin/photoscli tree
./bin/photoscli backup-all --out ./backup
./bin/photoscli backup-all --out ./backup --originals
./bin/photoscli export --album-id "<album-local-identifier>" --out ./export
./bin/photoscli export --album-id "Vacation" --out ./export
./bin/photoscli export --album-id "<album-local-identifier>" --out ./export --size 2048
./bin/photoscli export --album-id "<album-local-identifier>" --out ./export --originals

Commands

albums

  • Requests Photos access
  • Lists albums as tab-separated album ID and title

Example output:

5E9F.../L0/001	Vacation
8A1B.../L0/001	Work

photos --album-id <id-or-title>

  • Requests Photos access
  • If the value looks like a PhotoKit local identifier, uses it directly
  • Otherwise searches album titles for a match and resolves the identifier
  • Lists asset local identifiers and cloud status for the given album

Example output:

1F2A.../L0/001	IMG_0001.JPG	local
9C4D.../L0/001	IMG_0002.JPG	cloud

backup-all --out <dir> [--size <px>] [--originals]

  • Requests Photos access
  • Walks the Photos folder and album hierarchy
  • Creates directories as out/folder/album/files
  • Exports previews by default, originals when --originals is present
  • Shows per-asset progress bar with filename, file size, and cloud/local status
  • Uses --size only for preview export

Example layout:

backup/
  Trips/
    Italy 2024/
      Venice/
        0000_....jpg
  Favorites/
    0000_....jpg

export --album-id <id-or-title> --out <dir> [--size <px>] [--originals]

  • Requests Photos access
  • Resolves --album-id by local identifier first, then by album title if not found
  • Creates the output directory if needed
  • Exports assets one at a time with a progress bar: [=======---] 50% filename.jpg 1.2 MB cloud
  • Shows file size and cloud/local status for each exported asset
  • Exports resized JPEG previews by default
  • Exports original files when --originals is present
  • Writes a summary like exported 10 photos to ./export or exported 10 original files to ./export to stderr

--size is the target bounding box passed to PhotoKit for preview export. Default: 1024.

--originals switches export mode to original-file export. In that mode, --size is ignored.

tree

  • Requests Photos access
  • Prints folders and albums as an indented tree
  • Omits internal album IDs for human-readable output

Example output:

Trips
  Italy 2024
    Venice
Favorites

Permissions

On first use, macOS may prompt for Photos access.

If access is denied, the CLI returns an error telling you to grant access in:

System Settings > Privacy & Security

Export Details

Exported files currently:

  • are JPEGs when exporting previews
  • keep their original filenames when exporting originals when possible
  • fall back to a sanitized asset identifier if an original filename is unavailable
  • prefix duplicate original filenames with the asset index to avoid collisions
  • name preview exports like 0000_<asset-local-identifier>.jpg
  • replace / and \ in asset IDs with _ for generated preview filenames
  • replace / and \ in folder and album names with _ when creating backup directory names
  • preserve ordering based on ascending asset creation date

If some assets fail but at least one succeeds, the command still succeeds and reports the number exported.

If all exports fail, the command returns an error.

Signal Handling

Sending SIGINT (Ctrl+C) or SIGTERM during export or backup triggers a graceful shutdown:

  1. The CLI prints received signal, finishing current file... to stderr
  2. The current file export is allowed to complete
  3. No further files are started
  4. The process exits after the in-progress file finishes

A second signal forces an immediate exit.

Architecture

  • cmd/photoscli: CLI entrypoint, argument parsing, and album name resolution
  • internal/photos: Go bridge interface, JSON parsing, and error mapping
  • bridge/: Objective-C PhotoKit implementation plus a C test stub

Data passed from Objective-C to Go is serialized as JSON and unmarshaled into Go structs.

Known Limitations

  • The tree view only shows user collections exposed through PhotoKit's top-level user collections API
  • Album title resolution matches the first album with that title; if multiple albums share a title, use the local identifier instead
  • photos only prints asset IDs and filenames, not dates or metadata
  • Preview export uses PhotoKit preview rendering, not original file export
  • Original export currently writes the first PhotoKit asset resource for each asset, which may not capture every related representation for complex assets
  • iCloud-backed assets may require network download during export
  • A second interrupt signal forces an immediate exit without waiting for the current file
  • Partial export failures are not listed individually
S
Description
No description provided
Readme 3.4 MiB
v0.10.0 Latest
2026-06-15 06:27:38 +00:00
Languages
Go 90.8%
Objective-C 7.5%
C 1.2%
Makefile 0.5%