charmera

command module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 23, 2026 License: MIT Imports: 2 Imported by: 0

README

charmera

A macOS command-line tool & daemon that imports Kodak Charmera toy-camera photos and videos into the Photos app — repairing their broken EXIF metadata (in pure Go) plus optionally converting AVI clips to MP4 along the way. It can auto-run whenever the camera is plugged in, and never imports the same shot twice.

See install notes below.

This is a Go reimplementation of the Python kodak-charmera-exif-fixer, extended to import into Photos, handle different SD card disk names, and deduplicate against previous imports.

The problem

The Charmera's Generalplus chipset writes JPEGs with several EXIF defects, and the exact symptoms vary by unit/firmware:

Issue Fix
Malformed date 2026:03:03:12:16:29 (extra colon) Normalised to 2026:03:03 12:16:29
Missing capture date entirely Filled from the file's modification time
Wrong / missing ExifImageWidth/Height Set to the image's true pixel size
Corrupt MakerNote / bad IFD offsets Dropped — a fresh, clean EXIF block is written

AVI videos store an uncompressed Motion-JPEG + PCM stream and a hard-coded wrong date (2010). They're transcoded to H.264 + AAC with a correct creation_time.

In every case the image (pixel) data is preserved byte-for-byte — only the metadata is rewritten — and the camera card is never modified (read-only).

What it does

For each file on the camera, in one pass:

  1. Skip anything already in the Photos album (matched by a content hash embedded in the imported filename, so it works even after the camera renumbers files).
  2. Fix photo EXIF (pure Go) or convert AVI → MP4 (via ffmpeg) into a temporary staging area, named IMG_YYYYMMDD_HHMMSS_<hash>.jpg / VID_…​.mp4.
  3. Auto-rotate (optional) — detect each photo's orientation with a local ML model and set the EXIF Orientation tag so it shows upright in Photos.
  4. Import the fixed files into a Photos album (default: Kodak Charmera).
  5. Delete the staged copy — Photos holds the only copy. There's no separate database, so deleting a photo from the album re-enables its import next run.
  6. Unmount the camera when finished, so you can unplug it safely.

Install

Optional, all via Homebrew:

  • ffmpeg (brew install ffmpeg) — only if you have AVI videos to convert.
  • onnxruntime (brew install onnxruntime) — enables the orientation auto-rotate feature; the model itself (~77 MB) is downloaded automatically on first use.

EXIF fixing and Photos import need neither.

Download a release (no Go toolchain required) — grab the macOS archive from the Releases page (a single universal binary for Intel and Apple Silicon), then:

tar -xzf charmera_*_darwin_all.tar.gz
xattr -d com.apple.quarantine charmera   # the binary is unsigned; clear Gatekeeper
sudo mv charmera /usr/local/bin/

Or build from source. Requires Go and a C toolchain (Xcode Command Line Tools — run xcode-select --install), because the binary uses cgo for onnxruntime and the DiskArbitration framework. CGO_ENABLED=0 won't build.

go install github.com/jphastings/charmera@latest   # installs to $(go env GOPATH)/bin
# or, from a checkout:
go build -o charmera .

Usage

charmera                   # detect the camera and import everything new
charmera run --dry-run     # preview what would happen, change nothing
charmera run --out ./fixed # write fixed files to a folder instead of importing

Flags (for run):

Flag Default Description
--volume NAME auto-detect Pin to a specific volume name (override; see Detection)
--album NAME Kodak Charmera Photos album to import into
--out DIR Write fixed/converted files to DIR instead of importing
--dry-run Show planned actions without changing anything
--auto Non-interactive; exit quietly if no camera is mounted
--no-auto-rotate Disable orientation detection (on when onnxruntime + model are present)
--no-unmount Leave the camera mounted when finished

Auto-launch when the camera is plugged in

charmera install     # registers a LaunchAgent that watches /Volumes
charmera uninstall   # removes it

The agent watches /Volumes (not a fixed name), so it fires whenever any volume is mounted; each time, charmera detects the Charmera by signature and exits quietly if it isn't the one. Pin it to a single volume with charmera install --volume NAME if you prefer.

install records the path of the charmera binary, so install it to a stable location first (e.g. go install, or copy it to /usr/local/bin) and re-run install if you move it. Logs are written to ~/Library/Logs/charmera.*.log.

Permissions
  • The first import asks macOS for permission to control Photos (Automation). Grant it; under the LaunchAgent the prompt appears in your GUI session.
  • Keep Photos' "Copy items to the Photos library" setting enabled (the default). The tool deletes its staged copy after import, which is safe only when Photos has copied the file into its own library.

How it works

  • Detection (name-independent). The camera is found by content signature, not its volume name (which you can rename): a volume qualifies if it has a DCIM directory plus either a sibling SPIDCIM directory or a JPEG bearing the Generalplus GPEncoder comment. --volume NAME overrides this to pin a specific volume.
  • EXIF (pure Go). The JPEG is parsed into its marker segments; the true size is read from the Start-Of-Frame header. A brand-new little-endian EXIF block is written with the corrected date, dimensions and preserved Orientation, and spliced in as the APP1 segment. Because the EXIF is rebuilt from scratch, the camera's corrupt MakerNote/IFD is simply discarded rather than repaired.
  • Camera identity. The rebuilt EXIF also stamps the camera's published details: Make Kodak, Model Charmera, and the fixed lens (35 mm-equivalent, f/2.4).
  • Orientation (optional, local ML). When onnxruntime is installed, each photo is run through the deep-image-orientation-detection EfficientNetV2 model (downloaded once, run locally via onnxruntime) to predict 0°/90°/180°/270°. The 180° case is treated as impossible — a hand-held camera is never upside-down — so its probability is dropped and the rest renormalised. A rotation is written to the EXIF Orientation tag only when it then wins an outright majority (≥ 0.50), so an already-upright photo is never flipped.
  • Video. ffmpeg transcodes to H.264 (CRF 26) + AAC, stamping creation_time from the file's modification time (the embedded date is wrong).
  • Unmount. When finished, the camera volume is unmounted via the DiskArbitration framework (--no-unmount to skip).
  • Dedup (Photos is the source of truth). Each camera file is hashed (SHA-256) and the first 16 hex chars are embedded in the imported filename. Before importing, charmera asks Photos for the album's existing filenames and skips any whose hash is already present — so identical content is never imported twice (even across renames), and deleting a photo from the album re-enables its import. The import uses skip check duplicates so our filename hash is the sole authority: Photos' own duplicate memory (which persists even after deletion) can't silently block a re-import. There is no separate ledger; the only on-disk state is the staging area (~/Library/Application Support/charmera/staging), cleared after each run.

Development

go test ./...

The EXIF tests validate the rebuilt metadata against exiftool when it's installed (skipped otherwise), and the video test performs a real ffmpeg round-trip when ffmpeg is available.

License

MIT

Documentation

Overview

Command charmera imports Kodak Charmera photos and videos into the macOS Photos app, repairing their broken EXIF (pure Go) and converting AVI to MP4 (via ffmpeg) along the way.

Directories

Path Synopsis
internal
cli
Package cli implements the charmera command-line interface.
Package cli implements the charmera command-line interface.
config
Package config holds the runtime configuration for the charmera tool and its defaults.
Package config holds the runtime configuration for the charmera tool and its defaults.
exiffix
Package exiffix repairs the broken EXIF that Kodak Charmera (Generalplus CBB3) cameras produce, entirely in pure Go.
Package exiffix repairs the broken EXIF that Kodak Charmera (Generalplus CBB3) cameras produce, entirely in pure Go.
launchagent
Package launchagent installs and removes the macOS LaunchAgent that runs charmera automatically when the camera is plugged in.
Package launchagent installs and removes the macOS LaunchAgent that runs charmera automatically when the camera is plugged in.
orient
Package orient detects the correct orientation of a photo using the DuarteBarbosa/deep-image-orientation-detection EfficientNetV2 ONNX model, so a rotated shot can be tagged with the right EXIF Orientation.
Package orient detects the correct orientation of a photo using the DuarteBarbosa/deep-image-orientation-detection EfficientNetV2 ONNX model, so a rotated shot can be tagged with the right EXIF Orientation.
photos
Package photos imports media into the macOS Photos app via AppleScript (osascript), placing it in a named album that is created on first use.
Package photos imports media into the macOS Photos app via AppleScript (osascript), placing it in a named album that is created on first use.
pipeline
Package pipeline orchestrates the full Charmera workflow: scan the camera, skip anything already in the Photos album, fix EXIF / convert video, import into Photos, then discard the staged copy.
Package pipeline orchestrates the full Charmera workflow: scan the camera, skip anything already in the Photos album, fix EXIF / convert video, import into Photos, then discard the staged copy.
scan
Package scan finds and classifies the media files on a mounted Charmera, and computes the content hashes used for deduplication.
Package scan finds and classifies the media files on a mounted Charmera, and computes the content hashes used for deduplication.
video
Package video converts the Charmera's uncompressed Motion-JPEG/PCM AVI files to H.264/AAC MP4, setting a correct creation_time (the camera hard-codes a wrong date).
Package video converts the Charmera's uncompressed Motion-JPEG/PCM AVI files to H.264/AAC MP4, setting a correct creation_time (the camera hard-codes a wrong date).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL