Pulls images from a self-hosted Immich server and syncs them into a directory to be used as desktop wallpapers.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
rwxd 508d048b51
All checks were successful
Check and Release / check (push) Successful in 1m54s
Check and Release / semantic-release (push) Successful in 56s
chore: normalize EOF newlines
2026-10-11 13:42:12 +02:00
.forgejo/workflows chore: normalize EOF newlines 2026-10-11 13:42:12 +02:00
cmd feat: initial immich-wallpaper-sync 2026-10-11 13:27:50 +02:00
docs/adr feat: initial immich-wallpaper-sync 2026-10-11 13:27:50 +02:00
internal feat: initial immich-wallpaper-sync 2026-10-11 13:27:50 +02:00
.gitignore feat: initial immich-wallpaper-sync 2026-10-11 13:27:50 +02:00
.pre-commit-config.yaml feat: initial immich-wallpaper-sync 2026-10-11 13:27:50 +02:00
.releaserc.yml chore: normalize EOF newlines 2026-10-11 13:42:12 +02:00
AGENTS.md feat: initial immich-wallpaper-sync 2026-10-11 13:27:50 +02:00
config.example.yaml feat: initial immich-wallpaper-sync 2026-10-11 13:27:50 +02:00
go.mod build: require go 1.25 for nixpkgs compatibility 2026-10-11 13:40:12 +02:00
go.sum feat: initial immich-wallpaper-sync 2026-10-11 13:27:50 +02:00
LICENSE ci: add Forgejo check and semantic-release workflow 2026-10-11 13:42:05 +02:00
main.go feat: initial immich-wallpaper-sync 2026-10-11 13:27:50 +02:00
README.md docs: clarify LLM disclaimer tooling 2026-10-11 13:29:23 +02:00

immich-wallpaper-sync

Disclaimer: this entire tool was developed with opencode, using DeepSeek v4 flash and GLM 5.3 flash as planner. It was built for a personal setup and shared as-is — review the code before trusting it with your library, and report issues/PRs if you find bugs.

A small Go CLI that pulls images from a self-hosted Immich server and syncs them into a directory to be used as desktop wallpapers. Inspired by SONDLecT/immich-wallpaper (Android companion), reimplemented for the desktop.

It only downloads — applying the wallpaper to your desktop is your environment's job (see Applying the wallpapers).

Features

  • Pick from the whole library, favorites, an album, a person, or a CLIP smart search phrase — or combine them (search: phrases are OR'd).
  • One-shot fetch (cron/systemd friendly) or a long-running daemon that re-syncs every interval.
  • No-repeat rotation: recently shown assets and shooting dates are tracked in a state file, so a batch spans different places/sessions rather than one photo burst.
  • Orientation filtering (landscape / portrait / any).
  • Downloads originals for web-friendly formats (JPEG/PNG/WebP/GIF) and falls back to Immich's JPEG preview for everything else (HEIC, etc.).
  • Requires camera EXIF by default, filtering out memes, screenshots and downloaded internet images.
  • Read-only against Immich: only search + download endpoints are used.

Install

go install git.chaops.de/rwxd/immich-wallpaper-sync@latest

Or build from source:

git clone <this repo>
cd immich-wallpaper-sync
go build -o immich-wallpaper-sync .

Configuration

Connection info can come from CLI flags, environment variables, or a config file. Precedence: flag > env var > config file.

Environment variables use the IMMICH_ prefix (IMMICH_URL, IMMICH_API_KEY, IMMICH_SOURCE, ...). The config file lives at ~/.config/immich-wallpaper-sync/config.yaml (YAML or TOML), or wherever --config points. See config.example.yaml.

Create an API key in Immich under Account Settings → API Keys with the asset.view and asset.download (read) permissions.

Usage

Download a single random landscape photo:

immich-wallpaper-sync fetch \
  --url https://photos.example.com \
  --api-key "$IMMICH_API_KEY" \
  --source library \
  --orientation landscape

Re-sync every 30 minutes in the background:

immich-wallpaper-sync daemon --interval 30m --source "album:e6f24d17-..."

Downloads land in --out (default ~/.cache/immich-wallpaper-sync), named wallpaper-<assetid>.<ext>. The directory is pruned to --keep files (default 20).

Applying the wallpapers

The tool never sets a wallpaper; that's the user's job. Point your own wallpaper daemon or rotation loop at the output directory. For example, on Hyprland with awww/swww:

awww img "$(ls ~/.cache/immich-wallpaper-sync | shuf -n1)"

Sources

--source is a comma-separated list of terms:

Term Meaning
library the entire library (default)
favorites starred assets only
album:<id> assets in an album
person:<id> assets featuring a person
search:<phrase> CLIP smart search, e.g. search:beach

Example:

--source "favorites"
--source "person:389fa157-ee56-4356-a3a7-ab516660d5b3"
--source "favorites,album:e6f24d17-..."

Repeating search: ORs the results across phrases — each phrase is its own CLIP query and the candidate pools are unioned before a random pick. This fixes compound phrases like "beautiful nature and big architecture", which CLIP would otherwise mangle into one query:

# "beautiful nature" OR "big architecture"
--source "search:beautiful nature,search:big architecture"

RAW files are skipped, and --require-camera (default true) drops anything with no camera EXIF (memes, screenshots). If smart search matches nothing, the tool falls back to the library. --threshold (0..1) tightens CLIP relevance; higher numbers return only close matches.

Within a batch, picks are spread across distinct shooting dates and exact duplicates are deduped by checksum, so a run doesn't pull a whole photo burst/session from one place. Recently used dates and assets are suppressed via the state file (see --history).

Find album/person IDs via the Immich web UI URL, or:

curl -H "x-api-key: $IMMICH_API_KEY" https://photos.example.com/api/albums
curl -H "x-api-key: $IMMICH_API_KEY" https://photos.example.com/api/people

Scheduling with systemd (user timer)

~/.config/systemd/user/immich-wallpaper-sync.service:

[Unit]
Description=Sync an Immich wallpaper

[Service]
Type=oneshot
ExecStart=%h/go/bin/immich-wallpaper-sync fetch

~/.config/systemd/user/immich-wallpaper-sync.timer:

[Unit]
Description=Sync an Immich wallpaper hourly

[Timer]
OnBootSec=5min
OnUnitActiveSec=1h

[Install]
WantedBy=timers.target

Then systemctl --user enable --now immich-wallpaper-sync.timer. For a continuously running process, prefer immich-wallpaper-sync daemon as a long-running service.

How it works

fetch/daemon → parse source → query /api/search/random (or /api/search/smart for phrases) → filter orientation, camera EXIF and skip recently shown IDs/dates → download to the output directory → prune to --keep files. The state file (~/.cache/immich-wallpaper-sync/state.json) remembers recent assets and dates so the rotation does not repeat.

Flags

--url             Immich server URL
--api-key         Immich API key
--source          photo source (see table above)
--orientation     any|landscape|portrait
--size            original|preview
--threshold       smart search relevance 0..1 (0 = server default)
--out             download directory
--interval        daemon re-sync interval (e.g. 30m, 1h)
--batch           images to download per run
--keep            number of wallpapers to keep in the output directory
--history         distinct assets/dates to remember (suppress repeats)
--require-camera  only pick photos with camera EXIF (default true)
--config          config file path

License

MIT