Skip to content

feat(lightroom): auto-link Lightroom cloud photos and videos to dives#566

Merged
ericgriffin merged 22 commits into
mainfrom
lightroom-auto-linking
Jul 11, 2026
Merged

feat(lightroom): auto-link Lightroom cloud photos and videos to dives#566
ericgriffin merged 22 commits into
mainfrom
lightroom-auto-linking

Conversation

@ericgriffin

@ericgriffin ericgriffin commented Jul 11, 2026

Copy link
Copy Markdown
Member

Summary

Adobe Lightroom cloud auto-linking: connect a Lightroom catalog (BYO Adobe client ID), scan it manually or on a 6-hour startup poll, and photos/videos whose capture time falls inside a dive window [entry - 30 min, exit + 60 min] attach automatically. Confident matches link immediately with depth/temperature enrichment; ambiguous ones (overlapping windows on repetitive-dive days) become one-tap suggestions on the dive detail page. Rendition bytes flow through the #550 Media Store pipeline, so linked media displays on every synced device while the originals stay in Adobe's cloud.

Design spec: docs/superpowers/specs/2026-07-11-lightroom-auto-linking-design.md; implementation plan: docs/superpowers/plans/2026-07-11-lightroom-auto-linking.md (both already on main).

Architecture

Lightroom is an acquisition source, not a live dependency. The feature fills two sockets that already existed: the empty serviceConnector slot in MediaSourceResolverRegistry (new ConnectorMediaResolver, whose resolve() downloads the 2048 rendition), and MediaUploadPipeline._eligibleSources (#550), whose existing materialize -> hash -> dedup -> upload -> stamp flow now handles connector rows through that same resolver. Devices without the connector account display via the store fallback and never touch Adobe; disconnecting keeps every linked photo displaying from the store.

Changes

  • Schema v106: nullable connector_account_id/remote_asset_id on pending_photo_suggestions (PRAGMA-guarded idempotent DDL + beforeOpen re-assert; renumbered twice as the ladder climbed under it (v104 taken by the weight planner Weight prediction: personalized Weight Planner from dive history #564, v105 by the 3D flythrough heading migration Add per-sample compass heading capture (DC_SAMPLE_BEARING) #563); its test now holds the exact-latest version tripwire per the ladder convention)
  • lib/core/services/lightroom/: AdobeImsAuthManager (OAuth2+PKCE against IMS, paste-the-redirected-URL flow, refresh-token rotation persisted, optional client secret), LightroomAuthStore (secure-storage blob), LightroomApiClient (abuse-guard stripping, pagination, capture-date filters, renditions), models
  • PKCE helpers promoted from dropbox/ to shared core/services/oauth/oauth_pkce.dart
  • ConnectorAccountsRepository: first consumer of the v72 connector_accounts table
  • DivePhotoMatcher.matchTimestamp: confidence-bearing matching (unique extended-window hit or unique core hit = confident; else ambiguous, candidates ordered by entry proximity)
  • LightroomScanService: merged capture-window spans (one API query per boat day, not per dive), dedup by remoteAssetId against synced media rows BEFORE row creation (second-device scans are idempotent), attach + enrich + enqueue, suggestion fan-out, 90-day-lookback poll, confirmSuggestion
  • Connector videos are thumb-only through the pipeline: poster rendition hashed as content identity, thumb uploaded, remoteUploadedAt stays null so a future playback phase can tell posters from playable originals; backfill query gained the matching video-thumb clause
  • ThumbnailGenerator: connector renditions decode as JPEG regardless of originalFilename (a video's poster carries a .mp4 name and would otherwise never decode)
  • Settings -> Adobe Lightroom page: BYO client ID + optional secret, connect dialog, album filter, auto-poll toggle, last-poll line, needs-reauth chip, scan-now, disconnect; strings in all 11 locales
  • Scan actions on dive detail and trip overview; "Open in Lightroom" in the photo viewer (connected device only); suggestions row with accept/dismiss on dive detail
  • Startup auto-poll hook on the dive list page, gated on account + toggle + 6-hour interval, failures recorded to the connector state and never surfaced

Test Plan

  • flutter analyze clean; dart format . clean
  • Feature suites: oauth, lightroom services, media data/domain/presentation, media_store, database migrations (907 tests)
  • Adjacent suites: sync, trips, dive_log presentation (1913 tests)
  • Includes: two-device dedup, thumb-only video pipeline path, v104 migration + backstop, connect dialog flow, suggestion accept/dismiss, auto-poll gating

Manual smoke pending (live Adobe account required)

  1. Adobe Developer Console credential type that accepts Lightroom Services + PKCE (optional secret field hedges the Web App case)
  2. captured_before support on the assets endpoint (fallback documented in the plan if rejected)
  3. Video 2048 poster rendition existence (required by the thumb-only path)
  4. Real connect -> trip scan -> enrichment values; second device displays via store fallback
  5. updated_since as a cheaper poll cursor (future optimization; poll works without it)

…UD and remote-asset dedup queries (schema v104)
…rror paths, viewer action, entity equality, auto-poll failure
@ericgriffin ericgriffin force-pushed the lightroom-auto-linking branch from 14c04e1 to bed668c Compare July 11, 2026 19:09
@ericgriffin ericgriffin merged commit c13dc36 into main Jul 11, 2026
25 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in Submersion Release Tracker Jul 11, 2026
@ericgriffin ericgriffin deleted the lightroom-auto-linking branch July 11, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant