Synchronizing two Immich albums while on vacation

During our vacation we had the usual modern photo problem: everybody took pictures, more than one Immich server was involved, and the photos ended up in different shared albums. Immich can create public links with download and upload permission, but it cannot federate two installations or synchronize their albums. The wise developers of immich actively closed the feature request to add federation on github!

Downloading everything and uploading it again sounded easy until duplicates, videos and several gigabytes got involved. So during the vacation I started immich-sync (Source code).

Two share links are enough

The web page accepts two Immich public share links, including password-protected ones. It inspects both shares, compares the SHA-1 checksums already returned by the Immich API and shows which originals are missing on either side. The user can then copy left to right, right to left or in both directions.

It never deletes anything. Each missing original is downloaded into a temporary file and uploaded to the other share. Only one asset per job is staged, so synchronizing a large album does not require enough local disk for the entire vacation. Closing the browser’s WebSocket aborts the network requests and removes the temporary file.

One Immich optimization caused a nice bug: sending x-immich-checksum can make Immich report a duplicate before associating that existing asset with the destination shared album. I deliberately omit it. The normal upload path detects the duplicate and also adds it to the share, which is exactly what the synchronizer needs.

Of course it is C++ and Qt

The server is a small C++23 application using Qt 6 HttpServer, Network and WebSockets. The HTML, CSS and JavaScript frontend is embedded in the executable. Every WebSocket owns one short-lived synchronization session and its network clients; when the socket disappears, the whole job disappears with it.

There are no accounts and no recovery database. Share URLs and passwords are not stored on the server. The browser can put the two share URLs in its fragment for an easy retry or bookmark, but passwords are intentionally excluded.

The Kubernetes deployment runs two replicas. WebSockets do not need sticky sessions after the HTTP upgrade. The only shared persistent state is a tiny JSON file with numeric lifetime counters on CephFS; it never contains links, passwords, filenames or unfinished jobs. Prometheus metrics show active synchronizations, copied photos, videos and bytes.

Gitea builds an immutable image for every commit, pushes it to my Quay registry and updates the Kubernetes Deployment on main. The first working version went from empty repository to a usable deployed service on August 3. Not a bad vacation project, although downloading fewer photos would have been easier.


Beitrag veröffentlicht

in

von

Schlagwörter:

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert