rename applephotos to photoscli, update module path to gitea.k3s.k0.nu/tools/photocli

This commit is contained in:
Ein Anderssono
2026-06-11 20:29:06 +02:00
parent e48e20ad18
commit ca3a3e4a2a
6 changed files with 43 additions and 40 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
BINARY := ./bin/applephotos BINARY := ./bin/photoscli
MODULE := github.com/einand/applephotos MODULE := gitea.k3s.k0.nu/tools/photocli
BRIDGE_DIR := bridge BRIDGE_DIR := bridge
OBJ := $(BRIDGE_DIR)/photokit_bridge.o OBJ := $(BRIDGE_DIR)/photokit_bridge.o
LIB := $(BRIDGE_DIR)/libphotokit_bridge.a LIB := $(BRIDGE_DIR)/libphotokit_bridge.a
@@ -21,7 +21,7 @@ $(STUB_OBJ): $(BRIDGE_DIR)/photokit_bridge_stub.c $(BRIDGE_DIR)/photokit_bridge.
cc -c -o $@ $< cc -c -o $@ $<
build: $(LIB) build: $(LIB)
go build -o $(BINARY) $(MODULE)/cmd/applephotos go build -o $(BINARY) $(MODULE)/cmd/photoscli
test: $(STUB_LIB) test: $(STUB_LIB)
go test -tags=test -coverprofile=coverage.out -covermode=atomic -coverpkg=./... ./... go test -tags=test -coverprofile=coverage.out -covermode=atomic -coverpkg=./... ./...
+28 -25
View File
@@ -1,26 +1,26 @@
# applephotos # photoscli
`applephotos` is a small macOS-only CLI written in Go that reads data from Apple Photos through a PhotoKit bridge. `photoscli` is a small macOS-only CLI written in Go that reads data from Apple Photos through a PhotoKit bridge.
It supports five tasks: It supports five tasks:
- listing albums - listing albums
- listing asset IDs and filenames in an album - listing asset IDs, filenames, and cloud status in an album
- showing the folder and album tree - showing the folder and album tree
- backing up all albums into the Photos folder tree - backing up all albums into the Photos folder tree
- exporting resized JPEG previews or original files from an album - exporting resized JPEG previews or original files from an album
## What The Code Does ## What The Code Does
The executable lives in `cmd/applephotos` and calls into `internal/photos`, which wraps an Objective-C bridge in `bridge/`. The executable lives in `cmd/photoscli` and calls into `internal/photos`, which wraps an Objective-C bridge in `bridge/`.
Current behavior: Current behavior:
- `albums` prints one line per album as `<album-id>\t<title>` - `albums` prints one line per album as `<album-id>\t<title>`
- `photos --album-id <id-or-title>` prints one asset local identifier and filename per line; accepts either a PhotoKit local identifier or an album 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 - `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 under the output directory - `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 and reports the number exported on stderr; `--album-id` accepts either a PhotoKit local identifier or an album title - `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: The bridge uses PhotoKit to:
@@ -48,7 +48,7 @@ make build
Output binary: Output binary:
```bash ```bash
./bin/applephotos ./bin/photoscli
``` ```
## Test ## Test
@@ -62,16 +62,16 @@ Tests run against a stub bridge, so they do not require real Photos access.
## Usage ## Usage
```bash ```bash
./bin/applephotos albums ./bin/photoscli albums
./bin/applephotos photos --album-id "<album-local-identifier>" ./bin/photoscli photos --album-id "<album-local-identifier>"
./bin/applephotos photos --album-id "Vacation" ./bin/photoscli photos --album-id "Vacation"
./bin/applephotos tree ./bin/photoscli tree
./bin/applephotos backup-all --out ./backup ./bin/photoscli backup-all --out ./backup
./bin/applephotos backup-all --out ./backup --originals ./bin/photoscli backup-all --out ./backup --originals
./bin/applephotos export --album-id "<album-local-identifier>" --out ./export ./bin/photoscli export --album-id "<album-local-identifier>" --out ./export
./bin/applephotos export --album-id "Vacation" --out ./export ./bin/photoscli export --album-id "Vacation" --out ./export
./bin/applephotos export --album-id "<album-local-identifier>" --out ./export --size 2048 ./bin/photoscli export --album-id "<album-local-identifier>" --out ./export --size 2048
./bin/applephotos export --album-id "<album-local-identifier>" --out ./export --originals ./bin/photoscli export --album-id "<album-local-identifier>" --out ./export --originals
``` ```
### Commands ### Commands
@@ -93,13 +93,13 @@ Example output:
- Requests Photos access - Requests Photos access
- If the value looks like a PhotoKit local identifier, uses it directly - If the value looks like a PhotoKit local identifier, uses it directly
- Otherwise searches album titles for a match and resolves the identifier - Otherwise searches album titles for a match and resolves the identifier
- Lists asset local identifiers for the given album - Lists asset local identifiers and cloud status for the given album
Example output: Example output:
```text ```text
1F2A.../L0/001 IMG_0001.JPG 1F2A.../L0/001 IMG_0001.JPG local
9C4D.../L0/001 IMG_0002.JPG 9C4D.../L0/001 IMG_0002.JPG cloud
``` ```
`backup-all --out <dir> [--size <px>] [--originals]` `backup-all --out <dir> [--size <px>] [--originals]`
@@ -107,8 +107,8 @@ Example output:
- Requests Photos access - Requests Photos access
- Walks the Photos folder and album hierarchy - Walks the Photos folder and album hierarchy
- Creates directories as `out/folder/album/files` - Creates directories as `out/folder/album/files`
- Exports previews by default - Exports previews by default, originals when `--originals` is present
- Exports original files when `--originals` is present - Shows per-asset progress bar with filename, file size, and cloud/local status
- Uses `--size` only for preview export - Uses `--size` only for preview export
Example layout: Example layout:
@@ -128,6 +128,8 @@ backup/
- Requests Photos access - Requests Photos access
- Resolves `--album-id` by local identifier first, then by album title if not found - Resolves `--album-id` by local identifier first, then by album title if not found
- Creates the output directory if needed - 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 resized JPEG previews by default
- Exports original files when `--originals` is present - 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 - Writes a summary like `exported 10 photos to ./export` or `exported 10 original files to ./export` to stderr
@@ -187,7 +189,9 @@ Sending `SIGINT` (Ctrl+C) or `SIGTERM` during export or backup triggers a gracef
A second signal forces an immediate exit. A second signal forces an immediate exit.
- `cmd/applephotos`: CLI entrypoint, argument parsing, and album name resolution ## Architecture
- `cmd/photoscli`: CLI entrypoint, argument parsing, and album name resolution
- `internal/photos`: Go bridge interface, JSON parsing, and error mapping - `internal/photos`: Go bridge interface, JSON parsing, and error mapping
- `bridge/`: Objective-C PhotoKit implementation plus a C test stub - `bridge/`: Objective-C PhotoKit implementation plus a C test stub
@@ -201,6 +205,5 @@ Data passed from Objective-C to Go is serialized as JSON and unmarshaled into Go
- Preview export uses PhotoKit preview rendering, not original file export - 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 - 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 - iCloud-backed assets may require network download during export
- Export is synchronous and has no progress output
- A second interrupt signal forces an immediate exit without waiting for the current file - A second interrupt signal forces an immediate exit without waiting for the current file
- Partial export failures are not listed individually - Partial export failures are not listed individually
@@ -5,7 +5,7 @@ import (
"io" "io"
"strings" "strings"
"github.com/einand/applephotos/internal/photos" "gitea.k3s.k0.nu/tools/photocli/internal/photos"
) )
func run(args []string, stdout, stderr io.Writer, bridge photos.Bridge) int { func run(args []string, stdout, stderr io.Writer, bridge photos.Bridge) int {
@@ -37,14 +37,14 @@ func run(args []string, stdout, stderr io.Writer, bridge photos.Bridge) int {
} }
func usage(w io.Writer) { func usage(w io.Writer) {
fmt.Fprintln(w, `applephotos export optimized images from Apple Photos fmt.Fprintln(w, `photoscli export optimized images from Apple Photos
Usage: Usage:
applephotos albums photoscli albums
applephotos photos --album-id <id> photoscli photos --album-id <id>
applephotos tree photoscli tree
applephotos backup-all --out <dir> [--size <px>] [--originals] photoscli backup-all --out <dir> [--size <px>] [--originals]
applephotos export --album-id <id> --out <dir> [--size <px>] [--originals] photoscli export --album-id <id> --out <dir> [--size <px>] [--originals]
Commands: Commands:
albums List user-created albums albums List user-created albums
@@ -5,7 +5,7 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
"github.com/einand/applephotos/internal/photos" "gitea.k3s.k0.nu/tools/photocli/internal/photos"
) )
func main() { func main() {
@@ -8,7 +8,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/einand/applephotos/internal/photos" "gitea.k3s.k0.nu/tools/photocli/internal/photos"
) )
type mockBridge struct { type mockBridge struct {
@@ -89,7 +89,7 @@ func TestRunNoArgs(t *testing.T) {
if rc != 1 { if rc != 1 {
t.Errorf("rc = %d, want 1", rc) t.Errorf("rc = %d, want 1", rc)
} }
if !strings.Contains(stderr, "applephotos") { if !strings.Contains(stderr, "photoscli") {
t.Errorf("stderr should contain usage, got: %s", stderr) t.Errorf("stderr should contain usage, got: %s", stderr)
} }
} }
@@ -100,7 +100,7 @@ func TestRunHelp(t *testing.T) {
if rc != 0 { if rc != 0 {
t.Errorf("%s: rc = %d, want 0", cmd, rc) t.Errorf("%s: rc = %d, want 0", cmd, rc)
} }
if !strings.Contains(stderr, "applephotos") { if !strings.Contains(stderr, "photoscli") {
t.Errorf("%s: stderr should contain usage", cmd) t.Errorf("%s: stderr should contain usage", cmd)
} }
} }
+1 -1
View File
@@ -1,3 +1,3 @@
module github.com/einand/applephotos module gitea.k3s.k0.nu/tools/photocli
go 1.22 go 1.22