Skip to content

feat: add from-flickr adapter for importing Flickr account exports - #1355

Open
jmcauley wants to merge 18 commits into
simulot:mainfrom
jmcauley:add-flickr-adapter
Open

jmcauley wants to merge 18 commits into
simulot:mainfrom
jmcauley:add-flickr-adapter

Conversation

@jmcauley

@jmcauley jmcauley commented May 6, 2026

Copy link
Copy Markdown

Closes #1354

Summary

Adds a new immich-go upload from-flickr subcommand that imports a Flickr account export into Immich. Follows the same adapter pattern as from-google-photos.

Usage

Point at the folder containing your downloaded Flickr ZIPs:

immich-go upload from-flickr \
  --server http://your-immich:2283 \
  --api-key YOUR_KEY \
  ~/Downloads/flickr-export/

Individual files and globs also work — the adapter identifies the metadata archive automatically by the presence of albums.json, so order doesn't matter.

What gets imported

Flickr field Immich field
name Title / OriginalFileName (with original extension preserved)
description Description
date_taken CaptureDate (primary)
date_upload CaptureDate (fallback)
tags[].tag Tags
albums.json entries Albums (--sync-albums, default true)

EXIF data including GPS is preserved as-is by Immich during upload.

Known export quirks

  • Album membership — the albums field in per-photo JSON is always empty in real Flickr exports; membership is sourced exclusively from albums.json
  • People tags — Flickr's export always produces an empty people field even when photos have tagged users (Flickr export bug, not something we can work around)
  • GPS — geo field in per-photo JSON was empty in all tested exports; EXIF GPS is preserved normally

Changes

  • adapters/flickr/flickr.go — FlickrCmd struct, classifyArchives(), extractPhotoID(), two-pass Browse() goroutine
  • adapters/flickr/json.go — FlickrMetadata / FlickrAlbums JSON structs, AsMetadata() converter, albumIndex() helper
  • adapters/flickr/cmd.go — cobra wiring, --sync-albums flag, directory expansion for ZIP auto-discovery
  • adapters/flickr/flickr_test.go — 26 unit tests (archive classifier, ID extractor, Browse with fixtures)
  • app/upload/upload.go — register from-flickr subcommand
  • docs/misc/flickr-import.md — user-facing documentation

Testing

Tested against a real Flickr export (608 photos across 3 ZIPs):

  • 604 photos uploaded successfully on first run
  • Duplicate detection correctly identified photos already in Immich
  • Dates, titles, tags, and album assignments all verified in Immich UI
  • go test ./adapters/flickr/... — 26/26 pass
  • golangci-lint run ./adapters/flickr/... — 0 issues
  • go build ./... — clean

simulot and others added 18 commits November 23, 2025 15:42
Moves the Picasa.ini parsing logic to the top of the `parseDir` function to prevent the ini files from being discarded as useless.

This ensures that `.picasa.ini` files are processed before other file types are checked against banned files or deemed as useless.
Add: scripts/immich-provision.sh — provisions a fresh Immich instance, creates admin user, and updates .vscode/launch.json with the admin API key.

Add: scripts/immich-reset.sh — resets Immich data while preserving users and API keys.

Add: scripts/immich-cleanup.sh — shuts down Immich, removes volumes, and deletes the install directory.

Remove: legacy/maintenance scripts and e2e wrappers (scripts/check-immich-api.sh, scripts/delete_branches.sh, scripts/generate-release-notes.sh, scripts/publish-prerelease.sh, scripts/update-launch-with-key.sh, scripts/e2e/**, scripts/.github/immich-api-monitor/**).

Update: .vscode/tasks.json cleared (no tasks defined).

User-facing changes: New lifecycle scripts (provision, reset, cleanup). VS Code tasks removed; invoke scripts directly from the terminal.

Compatibility: Backward compatible for CLI usage; VS Code tasks are removed (intentional).
…tructions

chore: better copilot instructions
…dates

Introduce lightweight publish-subscribe event bus to decouple file processing
events from UI and logging consumers. Eliminates polling overhead and enables
real-time UI updates.

Core Infrastructure (Steps 1-4):
- Add Bus with Subscribe/Publish, buffered channels (1000 capacity)
- Add Event struct with Code, Time, File, Size, Args
- Extend Recorder with NewRecorderWithBus for dual slog+bus output
- Add FileProcessor.NewWithBus constructor for event-enabled processing
- Comprehensive tests: 5 bus tests, 3 AssetTracker tests

UI Migration (Step 5):
- Replace 100ms polling with event subscriptions (~90% CPU reduction)
- Remove logger hijacking (highJackLogger/restoreLogger deleted)
- Add batched log display (200ms flush, max 50 lines) prevents UI lag
- Add 250ms heartbeat refresh for smooth counter updates
- Unified size formatting via FormatEventBytes (UI consistency)
- Right-aligned counters with consistent %d formatting

AssetTracker Events (Step 7):
- Add AssetStateTransition{Processed,Discarded,Error} event codes
- AssetTracker.NewWithBus publishes state changes to bus
- Production code creates bus in upload.go initialization

Cleanup (Step 8):
- Remove fallback polling code (bus always present in production)
- Consolidate UI refresh logic into single refreshCounters() method
- Simplified goroutines assume bus existence (fail-fast)

Documentation (Step 9):
- Add docs/plans/2025-event-bus/README.md (architecture, usage, performance)
- Add logging-analysis.md (dual logging rationale)
- Add plan.md and progress.md (implementation tracking)
- Comprehensive GoDoc comments for Bus, Event, Subscription, Recorder
- GoDoc examples for NewWithBus constructors (FileProcessor, AssetTracker)

Performance:
- CPU idle: ~90% reduction (no polling tickers)
- UI lag: eliminated via batching (was freezing at 1000+ events/sec)
- Counter staleness: <250ms max (heartbeat interval)
- Event throughput: >10,000 events/sec before drops

Breaking Changes: None. Bus is opt-in via NewWithBus constructors.
Existing code using New() or NewRecorder() continues working unchanged.
feat(event-bus): implement event bus architecture for decoupled UI updates
Completed slices:
- S01: S01
- S02: S02
- S03: S03
- S04: S04

GSD-Milestone: M001-cir324
Branch: milestone/M001-cir324
…ickr photo title

When a photo has a title (md.Name), OriginalFileName was set to the bare
title string with no extension. TypeFromExt then returned an empty type,
causing every titled photo to fail with 'type file not supported'.

Append path.Ext(entry.base) to preserve the extension from the original
filename. Update TestBrowse fixture expectation to match.
Users download Flickr exports as multiple ZIPs manually from their account
page. Requiring them to list every filename is impractical, especially for
large accounts with many image archives.

expandDirArgs() checks each arg before ParsePath: if it's a directory,
glob *.zip inside and expand to individual paths. Non-directory args
(explicit ZIPs, globs) pass through unchanged.

Also updates Use string from '<metadata.zip> <images.zip>...' to
'<download-dir | zip-file>...' to reflect the new interface.
… descriptions

- Usage section now shows directory as primary form, with ZIP/glob as alternatives
- Clarified which filename pattern belongs to metadata vs image archives
  (72157*.zip is metadata, data-download-*.zip are images — was inverted)
- Added known limitation: directory scan is top-level only, no subdirectory recursion
- Reworded export description to reflect the manual download process
Flickr's export always produces an empty people field even when photos
have tagged users — confirmed with explicit tagging. Noted as a Flickr
export bug, not an adapter limitation.

Also clarified GPS: Flickr JSON geo field is empty, but EXIF GPS in the
image file itself is preserved by Immich during upload.
- gci: fix import ordering in flickr.go and json.go
- goconst: extract repeated "albums.json" string to albumsJSONFile constant
@jmcauley
jmcauley requested a review from simulot as a code owner May 6, 2026 05:27
@fdbuck0

fdbuck0 commented Sep 30, 2026

Copy link
Copy Markdown

I would be eternally grateful if this could be implemented. Trying to import a massive (100.000+ photo's) from Flickr into Immich

@simulot
simulot changed the base branch from develop to main October 17, 2026 20:53

This branch has not been deployed

No deployments
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.

feat: add from-flickr adapter for importing Flickr account exports

4 participants