Skip to content

Add Immich plugin to use Immich as a bidirectional UI for Elodie #496

@jmathai

Description

@jmathai

Summary

This PR adds an Immich plugin to Elodie that enables albums and favorites to be managed through Immich’s UI while ensuring:

  • All metadata is persisted in the photo itself
  • Elodie remains the canonical organizer
  • File moves do not break album or favorite state

Immich is treated as both:

  • an intent source (albums, favorites)
  • a materialized view target (albums rebuilt from metadata)

The plugin uses Elodie’s plugin database to track state and perform incremental syncs, avoiding full rescans on each run.


Metadata Contracts (Authoritative)

Album Metadata (Existing Elodie Behavior)

Elodie already supports album metadata using the following fields (checked in order):

  1. XMP-xmpDM:Album
  2. XMP:Album

The plugin must use these fields when writing album metadata.

Exactly one album per photo is supported.


Favorites Metadata

Immich does not support ratings; it supports favorites.

Mapping rules:

  • Immich isFavorite = true
    → write XMP:Rating = 5

  • Immich isFavorite = false
    remove XMP:Rating if present

Immich favorites are treated as the intent source, while XMP:Rating is the durable, file-backed representation.

This mapping must be enforced during Immich → Elodie sync.


Immich API Requirements

The plugin must use Immich’s API to retrieve:

  1. Albums

    • Album ID
    • Album name
    • Asset membership
    • updatedAt
  2. Assets

    • Asset ID
    • File path
    • isFavorite
    • updatedAt

A required endpoint is one that can retrieve all assets (or assets updated since a timestamp) so favorite changes can be detected incrementally.

The plugin must not assume Immich asset IDs are stable across file moves.


Plugin Database Usage (Required)

The plugin must use Elodie’s plugin database (see googlephotos plugin) to store:

  • Last successful sync timestamp
  • Asset ID → file path mapping
  • Last known album per asset
  • Last known favorite state
  • Last processed updatedAt per asset

This enables:

  • Incremental Immich API calls
  • Avoiding full library scans
  • Avoiding rereading metadata for all files on each run

Album Conflict Resolution

If an asset is in Album A and is added to Album B in Immich:

  • Log the conflict
  • Treat Album B as authoritative
  • Update the photo’s album metadata
  • Allow Elodie to move the photo into the new album

This preserves the “single album per photo” constraint while respecting user intent.


Supported User Actions

1. ./elodie.py batch

This command performs a one-time bootstrap sync from Elodie to Immich for albums and favorites. Once completed, this should be stored so that future calls to batch perform Immich to Elodie syncs only.

Elodie → Immich

Use Elodie's filesystem.get_all_files() generator to get all files and map to Immich asset by using originalFileName and originalPath from the POST /search/metadata API (here).

  • For photos with album metadata:

    • Ensure Immich album exists
    • Ensure photo is added to that album
  • For photos with XMP:Rating = 5:

    • Ensure Immich favorite is set
  • For photos without XMP:Rating:

    • Ensure Immich favorite is cleared

Immich → Elodie

  • Fetch only assets where:

    • updatedAt > last_sync_time
  • For changed assets:

    • If album membership changed:

      • Update photo album metadata
    • If isFavorite changed:

      • Write or remove XMP:Rating = 5

Optimization Requirements

  • Do not scan all Immich assets on each run

  • Do not reread metadata from all photos

    • Immich updatedAt
    • Plugin DB state
  • Full scan should only be required on first run when the plugin database does not have record that it's been completed.


Execution Order

For batch runs:

  1. If bootstrap has not run then perform Elodie to Immich sync.
  2. If bootstrap has run then fetch incremental changes from Immich
    2.1. Update photo metadata (albums, favorites) using .set_album() and a new .set_favorite() methods
    2.2. Run existing Elodie organization logic by calling filesystem.process_file() and making any updates to the plugin database to store the new path

Error Handling & Logging

  • Log (do not crash) on:

    • Missing files
    • Assets no longer managed by Elodie
    • Album conflicts
    • API failures
  • Provide summary output including:

    • Metadata updates
    • Album moves
    • Favorites set and cleared

Non-Goals

  • Preserving Immich asset IDs across file moves
  • Supporting multiple albums per photo
  • Real-time or webhook-based sync
  • Immich being a source of truth for metadata

Expected Outcome

After this PR:

  • Immich can be used safely as a UI for viewing and modifying albums and favorites
  • Favorites can be both set and cleared reliably
  • Elodie remains deterministic and metadata-driven
  • Album and favorite state survives file moves
  • Sync operations scale to very large libraries
  • Immich becomes a replaceable, reconstructible view layer

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions