feat: albums backed by InvokeAI image-gallery boards#331
Merged
Conversation
Adds a second album source type: instead of scanning a local directory tree, an album can mirror one or more boards on a running InvokeAI instance. Board contents are fetched via the InvokeAI HTTP API and resolved to local files under <invokeai_root>/outputs/images; the existing Embeddings pipeline indexes the explicit file list unchanged, so re-indexing naturally tracks board additions/removals. Backend: - New photomap/backend/invokeai_client.py: auth/token-cache helpers extracted from routers/invoke.py plus board-listing, image-name and image-delete wrappers usable outside the router layer. - Album model: source_type discriminator + per-album InvokeAI connection fields. image_paths and the index path are derived server-side (index lives in user_data_dir/indexes/<key>/, album key sanitized against traversal). Legacy YAML loads unchanged. - POST /invokeai/probe_status and /probe_boards let the album form validate a URL and list boards before anything is saved; stored passwords are resolved server-side, only ever paired with their own username, and never sent to the browser (album endpoints expose has_invokeai_password instead). - Deletion in board albums goes through the InvokeAI API so its database stays consistent; move_images is rejected; deleting a board album cleans up its derived index directory. - Indexing failures (unreachable backend, wrong root) fail fast with pointed progress errors and leave any existing index untouched. - Log lines that previously interpolated the full image-path list now summarize explicit lists (count + common parent). Frontend: - Add/edit album forms get a Directory / InvokeAI Board source toggle (immutable after creation), connection check, root-dir filetree picker, optional credentials, and a board multi-select checklist. Username/password defaults from the settings panel are surfaced in the form and cleared when the URL points elsewhere. - The add-form entrance animation no longer clamps the section at 600px (the taller board form previously overflowed it, hiding the Add Album button). - Indexing progress pollers re-resolve the album card on every tick, fixing a race where card rebuilds mid-indexing left the UI frozen at "Indexing in progress..."; freshly added albums are guarded against a duplicate, racing auto-index kickoff (stray 409 alerts). Tests: board-album CRUD/round-trip/password-leak guards, probe endpoints incl. credential-pairing rules, end-to-end board indexing / membership-update / deletion / move-rejection against a stubbed InvokeAI client, and Jest coverage for the form payloads, checklist, animation clamp and poller card resolution. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The assertions hardcoded POSIX separators, but str(Path(...)) and os.path.commonpath use backslashes on Windows. Build the expected strings from the same Path round-trips instead of literals. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a second album source type: an album can now mirror one or more InvokeAI gallery boards instead of scanning a local directory tree. When creating an album, a radio pair selects between Directory of Image Files (default, unchanged) and An InvokeAI Image Gallery Board.
For board albums, the form takes the InvokeAI backend URL (prefilled from Settings), the locally-accessible InvokeAI root directory (filetree picker — deliberately not fetched via the API), and optional credentials, then loads the board list into a multi-select checklist. Indexing fetches the boards' image names from the InvokeAI API, resolves them to
<root>/outputs/images/<name>, and feeds the explicit file list through the existingEmbeddingspipeline — so "Update Index" naturally picks up images added to or removed from the boards.Design decisions
DELETE /api/v1/images/i/{name}), never send2trash — deleting files directly would leave dangling rows in InvokeAI's database.move_imagesis rejected for board albums for the same reason.user_data_dir("photomap")/indexes/<album_key>/embeddings.npz), derived server-side; album keys are sanitized against path traversal, and deleting a board album removes its derived index directory.has_invokeai_passwordinstead of the password; the probe/board-listing endpoints resolve stored passwords server-side and only ever pair a stored password with its own username. The form surfaces this by prefilling the Settings username and noting the saved password will be used (cleared when the URL points at a different backend).image_pathsis derived as[<root>/outputs/images]so image serving and access validation work unchanged; HTTP helpers were extracted fromrouters/invoke.pyinto a sharedinvokeai_client.pyso the indexing/curation layers don't import a router.Robustness fixes found while testing against a live InvokeAI
forwardsfill), overflowing the taller board form and hiding the Add Album button.Tests
🤖 Generated with Claude Code