Skip to content

Add asset plugin type and first-party Immich plugin - #948

Open
slothful-vassal wants to merge 20 commits into
devfrom
immichIntegration
Open

Add asset plugin type and first-party Immich plugin#948
slothful-vassal wants to merge 20 commits into
devfrom
immichIntegration

Conversation

@slothful-vassal

@slothful-vassal slothful-vassal commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR introduces asset plugins, a new plugin category that connects wanderer to external media libraries, and ships Immich as the first integration: Immich suggests geotagged photos that match a trail or waypoint, and wanderer imports or links them as photo assets. This implements the feature requested in several issues (#160, #395). The plugin type is generic, so other libraries (e.g. PhotoPrism, #530) can follow without further core changes.

Data model & migration

  • New assets collection plus link collections for trails, waypoints, and summit logs, with access rules, APIs, and hooks.
  • Existing photos are migrated into asset records, and the legacy photos fields are removed from the trails, waypoints, and summit_logs collections — assets are now the single source of truth.
  • Backward compatibility: API responses still expose a photos array, now computed from asset links, and federation/full-sync converts legacy photos file URLs from older instances into assets.

Plugin system

  • New plugin type: "assets" with an asset_library.v1 capability; installed plugins are now categorized as trails or assets.
  • Host-owned configured connectors, asset import limits, and thumbnail/import/materialize/repair/delete flows. Plugin schema, SDK, and docs updated accordingly.
  • Two storage modes per asset plugin (photoMode): photos are either copied into wanderer (copy), or kept in the source library and referenced (link_private). Linked photos are materialized (copied in) before their target becomes public, and a guard prevents disabling a plugin while linked photos still depend on it.
  • Because any public or federated trail always materializes its photos, link_private only saves storage for trails that stay private. The settings UI states this so the tradeoff isn't surprising.

Immich plugin

  • Matches photos against the trail by capture time and GPS location; supports preview or original quality imports and an "own photos only" mode.
  • Uses the generic storage modes above; defaults to copy.
  • Least-privilege API key model — see upgrade notes below.

UI

  • Photo search and selection from the media library in the trail editor, waypoint attachment, and waypoint creation directly from photos.
  • Asset plugin settings, including admin actions to materialize, repair, or delete remote-linked photos in bulk (also triggered when disabling a plugin). These run asynchronously on the server; the settings page polls the job and shows a progress bar.
  • New data maintenance pages: duplicates, photo metadata backfill, trails without photos, and orphaned photos.

Existing flows

  • Trail imports and GPX uploads can auto-attach matching photos suggested by asset plugins; federation serializes/materializes asset photos; feed/profile/list APIs expose asset-backed photo URLs; GPX export includes asset photos; summit logs can link assets; trail merge preserves and reassigns photo assets.
  • Replaces the bundled exif-js vendor copy with ExifReader. This is required here: photo matching needs HEIC/HEIF metadata, which must be read before heic2any strips it during conversion. It also leaves wanderer with a single maintained EXIF reader instead of two stacks.

Reviewer guide

The diff is large (~200 files), but much of it is mechanical — existing photo code paths updated to read and write through the new asset links. Suggested entry points, in reading order:

  1. db/migrations/1778359896_created_assets.go — the assets collection, link collections, and legacy photo migration.
  2. db/pluginsystem/manifest.go and plugins/schema/plugin.schema.json — the assets plugin type and asset_library.v1 capability.
  3. db/routes/assets.go and db/routes/plugin_system_assets.go — asset APIs and the host-side search/import/materialize/repair/delete flows.
  4. plugins/immich/ — the plugin itself (API client in immich.go, time/location matching in matching.go).
  5. web/src/lib/components/photo/photo_picker.svelte — the central UI entry point for external photo selection.

Upgrade / review notes

  • The migration converts existing trail, waypoint, and summit-log photos into asset records and then drops the photos fields from those collections. The down migration intentionally does not undo this; upgrade with a full backup.
  • installed_plugins.type is updated so installed plugins are categorized as trails or assets.
  • Immich API keys need only user.read, asset.read, asset.view, and asset.download. No write, delete, upload, album, library, or admin scopes are required.
  • In link_private mode, photos stay backed by the source library only while the target remains non-public; publishing or federating a trail materializes (copies) its linked photos so public views can serve them safely. As a result, link_private reduces storage only for permanently-private trails.

Screenshots

(UI shown in German)

New Assets plugin category on the plugin settings page:

Plugin settings page with the new Assets section listing the Immich plugin

Immich plugin settings — storage mode, import size, auto-attach, and matching limits:

Immich settings dialog with server URL, API key, photo storage mode, import size, auto-attach toggles, and matching limits

New data maintenance pages — duplicates, photo metadata backfill, trails without photos, and orphaned photos:

Data maintenance: duplicate detection for routes and photos Data maintenance: backfill missing capture time and GPS data from local photo files Data maintenance: completed trails without photos, checked against enabled asset plugins Data maintenance: review and delete orphaned photos

Photos as a source everywhere — waypoints can be created from photos, and photo pickers offer the media library next to local files:

Waypoint section with a new 'from photos' menu: upload from device or choose from media library Trail editor photo field with local and media library sources

The "waypoints from photos" dialog with source filter, time range, map preview, and photo preview:

Waypoints-from-photos dialog: photo list filtered by source and time, map with photo clusters, large preview

Validation

Automated coverage was added or updated for asset migrations/share rules, asset helpers, federated asset identity, plugin manifests, asset plugin routes, asset merge, remote list/trail serialization, importer auto-attach, category preference filtering, and Immich matching.

Manual checks completed:

  • Ran migrations against fresh and existing databases; verified the assets collection, asset plugin type, and existing trail plugins.
  • Verified plugin listing/sorting, Immich configuration persistence, and enable/disable behavior.
  • Verified Immich connection and previews with only user.read, asset.read, asset.view, and asset.download.
  • Verified auto-attach defaults, explicit auto-attach opt-out, and time/location relevance filtering.
  • Verified trail editor photo picker, waypoint photo matching, direct photo waypoint creation, save/reopen persistence, and rendering.
  • Verified copied-photo storage mode.
  • Smoke-tested an existing trail-plugin flow after the generic plugin changes.

Manual checks still pending — should be completed before merging:

  • Invalid Immich URL/API key/missing-permission error handling.
  • Failed or empty import cleanup, especially avoiding orphaned/empty waypoints.
  • link_private access control for private trails, shared private trails, and publish transitions.
  • Remote asset lifecycle states for missing/inaccessible Immich assets.
  • Disable-plugin guard plus materialize/delete cleanup for linked remote photos.
  • Trail merge with copied and remote-linked assets.

@slothful-vassal
slothful-vassal marked this pull request as ready for review May 10, 2026 18:13
@slothful-vassal
slothful-vassal requested a review from Flomp May 10, 2026 18:15
@slothful-vassal
slothful-vassal marked this pull request as draft May 30, 2026 15:23

@slothful-vassal slothful-vassal left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

back to draft: try to implement as plugin

@slothful-vassal slothful-vassal linked an issue Jul 1, 2026 that may be closed by this pull request
… immichIntegration

# Conflicts:
#	web/src/lib/components/summit_log/summit_log_modal.svelte
#	web/src/lib/i18n/locales/no.json
#	web/src/lib/models/trail.ts
#	web/src/lib/stores/summit_log_store.ts
#	web/src/lib/stores/trail_store.ts
#	web/src/routes/trail/edit/[id]/+page.svelte
#	web/src/routes/trail/edit/[id]/+page.ts
@slothful-vassal slothful-vassal changed the title immich integration Add asset plugin type and first-party Immich plugin Jul 9, 2026
@slothful-vassal
slothful-vassal marked this pull request as ready for review July 9, 2026 17:07
@Flomp
Flomp changed the base branch from main to dev August 7, 2026 17:12
Flomp and others added 2 commits August 7, 2026 19:40
# Conflicts:
#	db/plugins/importer/importer.go
#	db/plugins/importer/importer_test.go
#	db/util/network_test.go
#	db/util/safe_fetch.go
#	web/src/lib/components/profile/feed_card.svelte
# Conflicts:
#	web/src/lib/components/trail/trail_info_panel.svelte
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Waypoints from photos: different EXIF tag

2 participants